diff --git a/src/core/package.cpp b/src/core/package.cpp index aca1c2f..a8072c2 100644 --- a/src/core/package.cpp +++ b/src/core/package.cpp @@ -295,15 +295,7 @@ namespace package { if (cargoPath.empty() || !fs::exists(cargoPath)) return Err(DracError(DracErrorCode::NotFound, "Could not find cargo directory")); - u64 count = 0; - - for (const fs::directory_entry& entry : fs::directory_iterator(cargoPath)) - if (entry.is_regular_file()) - ++count; - - debug_log("Found {} packages in cargo directory: {}", count, cargoPath.string()); - - return count; + return GetCountFromDirectory("cargo", cargoPath); } fn GetTotalCount() -> Result { diff --git a/src/os/macos.cpp b/src/os/macos.cpp index 5aa2060..cc6bb4f 100644 --- a/src/os/macos.cpp +++ b/src/os/macos.cpp @@ -5,229 +5,365 @@ #include #include -#include "macos/bridge.hpp" -#include "os.hpp" +#include "src/core/package.hpp" #include "src/util/defs.hpp" #include "src/util/error.hpp" +#include "src/util/helpers.hpp" #include "src/util/types.hpp" + +#include "macos/bridge.hpp" +#include "os.hpp" // clang-format on using namespace util::types; -using util::error::DracError; +using util::error::DracError, util::error::DracErrorCode; +using util::helpers::GetEnv; -fn os::GetMemInfo() -> Result { - u64 mem = 0; - usize size = sizeof(mem); +namespace { + fn StrEqualsIgnoreCase(std::string_view strA, std::string_view strB) -> bool { + if (strA.length() != strB.length()) + return false; - if (sysctlbyname("hw.memsize", &mem, &size, nullptr, 0) == -1) - return Err(DracError::withErrno("Failed to get memory info")); + return std::equal(strA.begin(), strA.end(), strB.begin(), [](char aChar, char bChar) { + return std::tolower(static_cast(aChar)) == std::tolower(static_cast(bChar)); + }); + } - return mem; -} + fn Capitalize(std::string_view sview) -> Option { + if (sview.empty()) + return None; -fn os::GetNowPlaying() -> Result { return GetCurrentPlayingInfo(); } + String result(sview); + result[0] = static_cast(std::toupper(static_cast(result[0]))); -fn os::GetOSVersion() -> Result { return GetMacOSVersion(); } + return result; + } +} // namespace -fn os::GetDesktopEnvironment() -> Result { - return "Aqua"; // TODO: Implement -} +namespace os { + fn GetMemInfo() -> Result { + u64 mem = 0; + usize size = sizeof(mem); -fn os::GetWindowManager() -> Result { - return "Yabai"; // TODO: Implement -} + if (sysctlbyname("hw.memsize", &mem, &size, nullptr, 0) == -1) + return Err(DracError("Failed to get memory info")); -fn os::GetKernelVersion() -> Result { - Array kernelVersion {}; - usize kernelVersionLen = sizeof(kernelVersion); + return mem; + } - if (sysctlbyname("kern.osrelease", kernelVersion.data(), &kernelVersionLen, nullptr, 0) == -1) - return Err(DracError::withErrno("Failed to get kernel version")); + fn GetNowPlaying() -> Result { return GetCurrentPlayingInfo(); } - return kernelVersion.data(); -} + fn GetOSVersion() -> Result { return GetMacOSVersion(); } -fn os::GetHost() -> Result { - Array hwModel {}; - usize hwModelLen = sizeof(hwModel); + fn GetDesktopEnvironment() -> Result { return "Aqua"; } - if (sysctlbyname("hw.model", hwModel.data(), &hwModelLen, nullptr, 0) == -1) - return Err(DracError::withErrno("Failed to get host info")); + fn GetWindowManager() -> Result { + constexpr std::array knownWms = { + "yabai", "kwm", "chunkwm", "amethyst", "spectacle", "rectangle", + }; - // taken from https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/host/host_mac.c - // shortened a lot of the entries to remove unnecessary info - std::flat_map modelNameByHwModel = { - // MacBook Pro - { "MacBookPro18,3", "MacBook Pro (14-inch, 2021)" }, - { "MacBookPro18,4", "MacBook Pro (14-inch, 2021)" }, - { "MacBookPro18,1", "MacBook Pro (16-inch, 2021)" }, - { "MacBookPro18,2", "MacBook Pro (16-inch, 2021)" }, - { "MacBookPro17,1", "MacBook Pro (13-inch, M1, 2020)" }, - { "MacBookPro16,3", "MacBook Pro (13-inch, 2020)" }, - { "MacBookPro16,2", "MacBook Pro (13-inch, 2020)" }, - { "MacBookPro16,4", "MacBook Pro (16-inch, 2019)" }, - { "MacBookPro16,1", "MacBook Pro (16-inch, 2019)" }, - { "MacBookPro15,4", "MacBook Pro (13-inch, 2019)" }, - { "MacBookPro15,3", "MacBook Pro (15-inch, 2019)" }, - { "MacBookPro15,2", "MacBook Pro (13-inch, 2018/2019)" }, - { "MacBookPro15,1", "MacBook Pro (15-inch, 2018/2019)" }, - { "MacBookPro14,3", "MacBook Pro (15-inch, 2017)" }, - { "MacBookPro14,2", "MacBook Pro (13-inch, 2017)" }, - { "MacBookPro14,1", "MacBook Pro (13-inch, 2017)" }, - { "MacBookPro13,3", "MacBook Pro (15-inch, 2016)" }, - { "MacBookPro13,2", "MacBook Pro (13-inch, 2016)" }, - { "MacBookPro13,1", "MacBook Pro (13-inch, 2016)" }, - { "MacBookPro12,1", "MacBook Pro (13-inch, 2015)" }, - { "MacBookPro11,4", "MacBook Pro (15-inch, 2015)" }, - { "MacBookPro11,5", "MacBook Pro (15-inch, 2015)" }, - { "MacBookPro11,2", "MacBook Pro (15-inch, 2013/2014)" }, - { "MacBookPro11,3", "MacBook Pro (15-inch, 2013/2014)" }, - { "MacBookPro11,1", "MacBook Pro (13-inch, 2013/2014)" }, - { "MacBookPro10,2", "MacBook Pro (13-inch, 2012/2013)" }, - { "MacBookPro10,1", "MacBook Pro (15-inch, 2012/2013)" }, - { "MacBookPro9,2", "MacBook Pro (13-inch, 2012)" }, - { "MacBookPro9,1", "MacBook Pro (15-inch, 2012)" }, - { "MacBookPro8,3", "MacBook Pro (17-inch, 2011)" }, - { "MacBookPro8,2", "MacBook Pro (15-inch, 2011)" }, - { "MacBookPro8,1", "MacBook Pro (13-inch, 2011)" }, - { "MacBookPro7,1", "MacBook Pro (13-inch, 2010)" }, - { "MacBookPro6,2", "MacBook Pro (15-inch, 2010)" }, - { "MacBookPro6,1", "MacBook Pro (17-inch, 2010)" }, - { "MacBookPro5,5", "MacBook Pro (13-inch, 2009)" }, - { "MacBookPro5,3", "MacBook Pro (15-inch, 2009)" }, - { "MacBookPro5,2", "MacBook Pro (17-inch, 2009)" }, - { "MacBookPro5,1", "MacBook Pro (15-inch, 2008)" }, - { "MacBookPro4,1", "MacBook Pro (17/15-inch, 2008)" }, + Array request = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; - // MacBook Air - { "MacBookAir10,1", "MacBook Air (M1, 2020)" }, - { "MacBookAir9,1", "MacBook Air (13-inch, 2020)" }, - { "MacBookAir8,2", "MacBook Air (13-inch, 2019)" }, - { "MacBookAir8,1", "MacBook Air (13-inch, 2018)" }, - { "MacBookAir7,2", "MacBook Air (13-inch, 2015/2017)" }, - { "MacBookAir7,1", "MacBook Air (11-inch, 2015)" }, - { "MacBookAir6,2", "MacBook Air (13-inch, 2013/2014)" }, - { "MacBookAir6,1", "MacBook Air (11-inch, 2013/2014)" }, - { "MacBookAir5,2", "MacBook Air (13-inch, 2012)" }, - { "MacBookAir5,1", "MacBook Air (11-inch, 2012)" }, - { "MacBookAir4,2", "MacBook Air (13-inch, 2011)" }, - { "MacBookAir4,1", "MacBook Air (11-inch, 2011)" }, - { "MacBookAir3,2", "MacBook Air (13-inch, 2010)" }, - { "MacBookAir3,1", "MacBook Air (11-inch, 2010)" }, - { "MacBookAir2,1", "MacBook Air (2009)" }, + usize len = 0; - // Mac mini - { "Macmini9,1", "Mac mini (M1, 2020)" }, - { "Macmini8,1", "Mac mini (2018)" }, - { "Macmini7,1", "Mac mini (2014)" }, - { "Macmini6,1", "Mac mini (2012)" }, - { "Macmini6,2", "Mac mini (2012)" }, - { "Macmini5,1", "Mac mini (2011)" }, - { "Macmini5,2", "Mac mini (2011)" }, - { "Macmini4,1", "Mac mini (2010)" }, - { "Macmini3,1", "Mac mini (2009)" }, + if (sysctl(request.data(), request.size(), nullptr, &len, nullptr, 0) == -1) + return Err(DracError("sysctl size query failed for KERN_PROC_ALL")); - // MacBook - { "MacBook10,1", "MacBook (12-inch, 2017)" }, - { "MacBook9,1", "MacBook (12-inch, 2016)" }, - { "MacBook8,1", "MacBook (12-inch, 2015)" }, - { "MacBook7,1", "MacBook (13-inch, 2010)" }, - { "MacBook6,1", "MacBook (13-inch, 2009)" }, - { "MacBook5,2", "MacBook (13-inch, 2009)" }, + if (len == 0) + return Err(DracError(DracErrorCode::NotFound, "sysctl for KERN_PROC_ALL returned zero length")); - // Mac Pro - { "MacPro7,1", "Mac Pro (2019)" }, - { "MacPro6,1", "Mac Pro (2013)" }, - { "MacPro5,1", "Mac Pro (2010 - 2012)" }, - { "MacPro4,1", "Mac Pro (2009)" }, + Vec buf(len); - // Mac (Generic) - { "Mac16,3", "iMac (24-inch, 2024)" }, - { "Mac16,2", "iMac (24-inch, 2024)" }, - { "Mac16,1", "MacBook Pro (14-inch, 2024)" }, - { "Mac16,6", "MacBook Pro (14-inch, 2024)" }, - { "Mac16,8", "MacBook Pro (14-inch, 2024)" }, - { "Mac16,7", "MacBook Pro (16-inch, 2024)" }, - { "Mac16,5", "MacBook Pro (16-inch, 2024)" }, - { "Mac16,15", "Mac mini (2024)" }, - { "Mac16,10", "Mac mini (2024)" }, - { "Mac15,13", "MacBook Air (15-inch, M3, 2024)" }, - { "Mac15,2", "MacBook Air (13-inch, M3, 2024)" }, - { "Mac15,3", "MacBook Pro (14-inch, Nov 2023)" }, - { "Mac15,4", "iMac (24-inch, 2023)" }, - { "Mac15,5", "iMac (24-inch, 2023)" }, - { "Mac15,6", "MacBook Pro (14-inch, Nov 2023)" }, - { "Mac15,8", "MacBook Pro (14-inch, Nov 2023)" }, - { "Mac15,10", "MacBook Pro (14-inch, Nov 2023)" }, - { "Mac15,7", "MacBook Pro (16-inch, Nov 2023)" }, - { "Mac15,9", "MacBook Pro (16-inch, Nov 2023)" }, - { "Mac15,11", "MacBook Pro (16-inch, Nov 2023)" }, - { "Mac14,15", "MacBook Air (15-inch, M2, 2023)" }, - { "Mac14,14", "Mac Studio (M2 Ultra, 2023)" }, - { "Mac14,13", "Mac Studio (M2 Max, 2023)" }, - { "Mac14,8", "Mac Pro (2023)" }, - { "Mac14,6", "MacBook Pro (16-inch, 2023)" }, - { "Mac14,10", "MacBook Pro (16-inch, 2023)" }, - { "Mac14,5", "MacBook Pro (14-inch, 2023)" }, - { "Mac14,9", "MacBook Pro (14-inch, 2023)" }, - { "Mac14,3", "Mac mini (M2, 2023)" }, - { "Mac14,12", "Mac mini (M2, 2023)" }, - { "Mac14,7", "MacBook Pro (13-inch, M2, 2022)" }, - { "Mac14,2", "MacBook Air (M2, 2022)" }, - { "Mac13,1", "Mac Studio (M1 Max, 2022)" }, - { "Mac13,2", "Mac Studio (M1 Ultra, 2022)" }, + if (sysctl(request.data(), request.size(), buf.data(), &len, nullptr, 0) == -1) + return Err(DracError("sysctl data fetch failed for KERN_PROC_ALL")); - // iMac - { "iMac21,1", "iMac (24-inch, M1, 2021)" }, - { "iMac21,2", "iMac (24-inch, M1, 2021)" }, - { "iMac20,1", "iMac (27-inch, 2020)" }, - { "iMac20,2", "iMac (27-inch, 2020)" }, - { "iMac19,1", "iMac (27-inch, 2019)" }, - { "iMac19,2", "iMac (21.5-inch, 2019)" }, - { "iMacPro1,1", "iMac Pro (2017)" }, - { "iMac18,3", "iMac (27-inch, 2017)" }, - { "iMac18,2", "iMac (21.5-inch, 2017)" }, - { "iMac18,1", "iMac (21.5-inch, 2017)" }, - { "iMac17,1", "iMac (27-inch, 2015)" }, - { "iMac16,2", "iMac (21.5-inch, 2015)" }, - { "iMac16,1", "iMac (21.5-inch, 2015)" }, - { "iMac15,1", "iMac (27-inch, 2014/2015)" }, - { "iMac14,4", "iMac (21.5-inch, 2014)" }, - { "iMac14,2", "iMac (27-inch, 2013)" }, - { "iMac14,1", "iMac (21.5-inch, 2013)" }, - { "iMac13,2", "iMac (27-inch, 2012)" }, - { "iMac13,1", "iMac (21.5-inch, 2012)" }, - { "iMac12,2", "iMac (27-inch, 2011)" }, - { "iMac12,1", "iMac (21.5-inch, 2011)" }, - { "iMac11,3", "iMac (27-inch, 2010)" }, - { "iMac11,2", "iMac (21.5-inch, 2010)" }, - { "iMac10,1", "iMac (27/21.5-inch, 2009)" }, - { "iMac9,1", "iMac (24/20-inch, 2009)" }, - }; + if (len % sizeof(kinfo_proc) != 0) + return Err(DracError( + DracErrorCode::PlatformSpecific, + std::format("sysctl returned size {} which is not a multiple of kinfo_proc size {}", len, sizeof(kinfo_proc)) + )); - const auto iter = modelNameByHwModel.find(hwModel.data()); - if (iter == modelNameByHwModel.end()) - return Err(DracError::withErrno("Failed to get host info")); + usize count = len / sizeof(kinfo_proc); - return String(iter->second); -} + std::span processes = std::span( + reinterpret_cast(buf.data()), count // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + ); -fn os::GetDiskUsage() -> Result { - struct statvfs vfs; + for (const kinfo_proc& procInfo : processes) { + StringView comm(procInfo.kp_proc.p_comm); - if (statvfs("/", &vfs) != 0) - return Err(DracError::withErrno("Failed to get disk usage")); + for (const auto& wmName : knownWms) + if (StrEqualsIgnoreCase(comm, wmName)) { + const auto capitalized = Capitalize(comm); + return capitalized ? Result(*capitalized) + : Err(DracError(DracErrorCode::ParseError, "Failed to capitalize window manager name")); + } + } - return DiskSpace { .used_bytes = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize, - .total_bytes = vfs.f_blocks * vfs.f_frsize }; -} + return "Quartz"; + } -fn os::GetPackageCount() -> Result { - return Err(DracError(DracErrorCode::NotSupported, "Package count is not supported on macOS")); // TODO: Implement -} + fn GetKernelVersion() -> Result { + Array kernelVersion {}; + usize kernelVersionLen = sizeof(kernelVersion); -fn os::GetShell() -> Result { - return "Fish"; // TODO: Implement -} + if (sysctlbyname("kern.osrelease", kernelVersion.data(), &kernelVersionLen, nullptr, 0) == -1) + return Err(DracError("Failed to get kernel version")); + + return kernelVersion.data(); + } + + fn GetHost() -> Result { + Array hwModel {}; + usize hwModelLen = sizeof(hwModel); + + if (sysctlbyname("hw.model", hwModel.data(), &hwModelLen, nullptr, 0) == -1) + return Err(DracError("Failed to get host info")); + + // taken from https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/host/host_mac.c + // shortened a lot of the entries to remove unnecessary info + std::flat_map modelNameByHwModel = { + // MacBook Pro + { "MacBookPro18,3", "MacBook Pro (14-inch, 2021)" }, + { "MacBookPro18,4", "MacBook Pro (14-inch, 2021)" }, + { "MacBookPro18,1", "MacBook Pro (16-inch, 2021)" }, + { "MacBookPro18,2", "MacBook Pro (16-inch, 2021)" }, + { "MacBookPro17,1", "MacBook Pro (13-inch, M1, 2020)" }, + { "MacBookPro16,3", "MacBook Pro (13-inch, 2020)" }, + { "MacBookPro16,2", "MacBook Pro (13-inch, 2020)" }, + { "MacBookPro16,4", "MacBook Pro (16-inch, 2019)" }, + { "MacBookPro16,1", "MacBook Pro (16-inch, 2019)" }, + { "MacBookPro15,4", "MacBook Pro (13-inch, 2019)" }, + { "MacBookPro15,3", "MacBook Pro (15-inch, 2019)" }, + { "MacBookPro15,2", "MacBook Pro (13-inch, 2018/2019)" }, + { "MacBookPro15,1", "MacBook Pro (15-inch, 2018/2019)" }, + { "MacBookPro14,3", "MacBook Pro (15-inch, 2017)" }, + { "MacBookPro14,2", "MacBook Pro (13-inch, 2017)" }, + { "MacBookPro14,1", "MacBook Pro (13-inch, 2017)" }, + { "MacBookPro13,3", "MacBook Pro (15-inch, 2016)" }, + { "MacBookPro13,2", "MacBook Pro (13-inch, 2016)" }, + { "MacBookPro13,1", "MacBook Pro (13-inch, 2016)" }, + { "MacBookPro12,1", "MacBook Pro (13-inch, 2015)" }, + { "MacBookPro11,4", "MacBook Pro (15-inch, 2015)" }, + { "MacBookPro11,5", "MacBook Pro (15-inch, 2015)" }, + { "MacBookPro11,2", "MacBook Pro (15-inch, 2013/2014)" }, + { "MacBookPro11,3", "MacBook Pro (15-inch, 2013/2014)" }, + { "MacBookPro11,1", "MacBook Pro (13-inch, 2013/2014)" }, + { "MacBookPro10,2", "MacBook Pro (13-inch, 2012/2013)" }, + { "MacBookPro10,1", "MacBook Pro (15-inch, 2012/2013)" }, + { "MacBookPro9,2", "MacBook Pro (13-inch, 2012)" }, + { "MacBookPro9,1", "MacBook Pro (15-inch, 2012)" }, + { "MacBookPro8,3", "MacBook Pro (17-inch, 2011)" }, + { "MacBookPro8,2", "MacBook Pro (15-inch, 2011)" }, + { "MacBookPro8,1", "MacBook Pro (13-inch, 2011)" }, + { "MacBookPro7,1", "MacBook Pro (13-inch, 2010)" }, + { "MacBookPro6,2", "MacBook Pro (15-inch, 2010)" }, + { "MacBookPro6,1", "MacBook Pro (17-inch, 2010)" }, + { "MacBookPro5,5", "MacBook Pro (13-inch, 2009)" }, + { "MacBookPro5,3", "MacBook Pro (15-inch, 2009)" }, + { "MacBookPro5,2", "MacBook Pro (17-inch, 2009)" }, + { "MacBookPro5,1", "MacBook Pro (15-inch, 2008)" }, + { "MacBookPro4,1", "MacBook Pro (17/15-inch, 2008)" }, + + // MacBook Air + { "MacBookAir10,1", "MacBook Air (M1, 2020)" }, + { "MacBookAir9,1", "MacBook Air (13-inch, 2020)" }, + { "MacBookAir8,2", "MacBook Air (13-inch, 2019)" }, + { "MacBookAir8,1", "MacBook Air (13-inch, 2018)" }, + { "MacBookAir7,2", "MacBook Air (13-inch, 2015/2017)" }, + { "MacBookAir7,1", "MacBook Air (11-inch, 2015)" }, + { "MacBookAir6,2", "MacBook Air (13-inch, 2013/2014)" }, + { "MacBookAir6,1", "MacBook Air (11-inch, 2013/2014)" }, + { "MacBookAir5,2", "MacBook Air (13-inch, 2012)" }, + { "MacBookAir5,1", "MacBook Air (11-inch, 2012)" }, + { "MacBookAir4,2", "MacBook Air (13-inch, 2011)" }, + { "MacBookAir4,1", "MacBook Air (11-inch, 2011)" }, + { "MacBookAir3,2", "MacBook Air (13-inch, 2010)" }, + { "MacBookAir3,1", "MacBook Air (11-inch, 2010)" }, + { "MacBookAir2,1", "MacBook Air (2009)" }, + + // Mac mini + { "Macmini9,1", "Mac mini (M1, 2020)" }, + { "Macmini8,1", "Mac mini (2018)" }, + { "Macmini7,1", "Mac mini (2014)" }, + { "Macmini6,1", "Mac mini (2012)" }, + { "Macmini6,2", "Mac mini (2012)" }, + { "Macmini5,1", "Mac mini (2011)" }, + { "Macmini5,2", "Mac mini (2011)" }, + { "Macmini4,1", "Mac mini (2010)" }, + { "Macmini3,1", "Mac mini (2009)" }, + + // MacBook + { "MacBook10,1", "MacBook (12-inch, 2017)" }, + { "MacBook9,1", "MacBook (12-inch, 2016)" }, + { "MacBook8,1", "MacBook (12-inch, 2015)" }, + { "MacBook7,1", "MacBook (13-inch, 2010)" }, + { "MacBook6,1", "MacBook (13-inch, 2009)" }, + { "MacBook5,2", "MacBook (13-inch, 2009)" }, + + // Mac Pro + { "MacPro7,1", "Mac Pro (2019)" }, + { "MacPro6,1", "Mac Pro (2013)" }, + { "MacPro5,1", "Mac Pro (2010 - 2012)" }, + { "MacPro4,1", "Mac Pro (2009)" }, + + // Mac (Generic) + { "Mac16,3", "iMac (24-inch, 2024)" }, + { "Mac16,2", "iMac (24-inch, 2024)" }, + { "Mac16,1", "MacBook Pro (14-inch, 2024)" }, + { "Mac16,6", "MacBook Pro (14-inch, 2024)" }, + { "Mac16,8", "MacBook Pro (14-inch, 2024)" }, + { "Mac16,7", "MacBook Pro (16-inch, 2024)" }, + { "Mac16,5", "MacBook Pro (16-inch, 2024)" }, + { "Mac16,15", "Mac mini (2024)" }, + { "Mac16,10", "Mac mini (2024)" }, + { "Mac15,13", "MacBook Air (15-inch, M3, 2024)" }, + { "Mac15,2", "MacBook Air (13-inch, M3, 2024)" }, + { "Mac15,3", "MacBook Pro (14-inch, Nov 2023)" }, + { "Mac15,4", "iMac (24-inch, 2023)" }, + { "Mac15,5", "iMac (24-inch, 2023)" }, + { "Mac15,6", "MacBook Pro (14-inch, Nov 2023)" }, + { "Mac15,8", "MacBook Pro (14-inch, Nov 2023)" }, + { "Mac15,10", "MacBook Pro (14-inch, Nov 2023)" }, + { "Mac15,7", "MacBook Pro (16-inch, Nov 2023)" }, + { "Mac15,9", "MacBook Pro (16-inch, Nov 2023)" }, + { "Mac15,11", "MacBook Pro (16-inch, Nov 2023)" }, + { "Mac14,15", "MacBook Air (15-inch, M2, 2023)" }, + { "Mac14,14", "Mac Studio (M2 Ultra, 2023)" }, + { "Mac14,13", "Mac Studio (M2 Max, 2023)" }, + { "Mac14,8", "Mac Pro (2023)" }, + { "Mac14,6", "MacBook Pro (16-inch, 2023)" }, + { "Mac14,10", "MacBook Pro (16-inch, 2023)" }, + { "Mac14,5", "MacBook Pro (14-inch, 2023)" }, + { "Mac14,9", "MacBook Pro (14-inch, 2023)" }, + { "Mac14,3", "Mac mini (M2, 2023)" }, + { "Mac14,12", "Mac mini (M2, 2023)" }, + { "Mac14,7", "MacBook Pro (13-inch, M2, 2022)" }, + { "Mac14,2", "MacBook Air (M2, 2022)" }, + { "Mac13,1", "Mac Studio (M1 Max, 2022)" }, + { "Mac13,2", "Mac Studio (M1 Ultra, 2022)" }, + + // iMac + { "iMac21,1", "iMac (24-inch, M1, 2021)" }, + { "iMac21,2", "iMac (24-inch, M1, 2021)" }, + { "iMac20,1", "iMac (27-inch, 2020)" }, + { "iMac20,2", "iMac (27-inch, 2020)" }, + { "iMac19,1", "iMac (27-inch, 2019)" }, + { "iMac19,2", "iMac (21.5-inch, 2019)" }, + { "iMacPro1,1", "iMac Pro (2017)" }, + { "iMac18,3", "iMac (27-inch, 2017)" }, + { "iMac18,2", "iMac (21.5-inch, 2017)" }, + { "iMac18,1", "iMac (21.5-inch, 2017)" }, + { "iMac17,1", "iMac (27-inch, 2015)" }, + { "iMac16,2", "iMac (21.5-inch, 2015)" }, + { "iMac16,1", "iMac (21.5-inch, 2015)" }, + { "iMac15,1", "iMac (27-inch, 2014/2015)" }, + { "iMac14,4", "iMac (21.5-inch, 2014)" }, + { "iMac14,2", "iMac (27-inch, 2013)" }, + { "iMac14,1", "iMac (21.5-inch, 2013)" }, + { "iMac13,2", "iMac (27-inch, 2012)" }, + { "iMac13,1", "iMac (21.5-inch, 2012)" }, + { "iMac12,2", "iMac (27-inch, 2011)" }, + { "iMac12,1", "iMac (21.5-inch, 2011)" }, + { "iMac11,3", "iMac (27-inch, 2010)" }, + { "iMac11,2", "iMac (21.5-inch, 2010)" }, + { "iMac10,1", "iMac (27/21.5-inch, 2009)" }, + { "iMac9,1", "iMac (24/20-inch, 2009)" }, + }; + + const auto iter = modelNameByHwModel.find(hwModel.data()); + if (iter == modelNameByHwModel.end()) + return Err(DracError("Failed to get host info")); + + return String(iter->second); + } + + fn GetDiskUsage() -> Result { + struct statvfs vfs; + + if (statvfs("/", &vfs) != 0) + return Err(DracError("Failed to get disk usage")); + + return DiskSpace { .usedBytes = (vfs.f_blocks - vfs.f_bfree) * vfs.f_frsize, + .totalBytes = vfs.f_blocks * vfs.f_frsize }; + } + + fn GetShell() -> Result { + if (const Result shellPath = GetEnv("SHELL")) { + // clang-format off + constexpr Array, 5> shellMap {{ + { "bash", "Bash" }, + { "zsh", "Zsh" }, + { "fish", "Fish" }, + { "nu", "Nushell" }, + { "sh", "SH" }, // sh last because other shells contain "sh" + }}; + // clang-format on + + for (const auto& [exe, name] : shellMap) + if (shellPath->contains(exe)) + return String(name); + + return *shellPath; // fallback to the raw shell path + } + + return Err(DracError(DracErrorCode::NotFound, "Could not find SHELL environment variable")); + } +}; // namespace os + +namespace package { + fn GetHomebrewCount() -> Result { + u64 count = 0; + + Array cellarPaths { + "/opt/homebrew/Cellar", + "/usr/local/Cellar", + }; + + for (const auto& cellarPath : cellarPaths) { + if (std::error_code errc; !fs::exists(cellarPath, errc) || errc) { + if (errc && errc != std::errc::no_such_file_or_directory) + return Err(DracError(errc)); + + return Err(DracError(DracErrorCode::NotFound, "Homebrew Cellar directory not found at: " + cellarPath.string()) + ); + } + + Result dirCount = GetCountFromDirectory("homebrew", cellarPath, true); + + if (!dirCount) { + if (dirCount.error().code != DracErrorCode::NotFound) + return dirCount; + + continue; + } + + count += *dirCount; + } + + return count; + } + + fn GetMacPortsCount() -> Result { + const PackageManagerInfo macPortsInfo = { + .id = "macports", + .dbPath = "/opt/local/var/macports/registry/registry.db", + .countQuery = "SELECT COUNT(*) FROM ports WHERE state='installed';", + }; + + // Check if the database file exists before trying to query + std::error_code errc; + if (!fs::exists(macPortsInfo.dbPath, errc) || errc) { + if (errc && errc != std::errc::no_such_file_or_directory) + return Err(DracError(errc)); + + return Err( + DracError(DracErrorCode::NotFound, "MacPorts registry database not found at: " + macPortsInfo.dbPath.string()) + ); + } + + return GetCountFromDb(macPortsInfo); + } +} // namespace package #endif diff --git a/src/util/error.hpp b/src/util/error.hpp index 5b43c44..29a5312 100644 --- a/src/util/error.hpp +++ b/src/util/error.hpp @@ -99,6 +99,7 @@ namespace util { : message(std::format("{}: {}", context, std::system_category().message(errno))), location(loc) { using namespace matchit; using enum DracErrorCode; + code = match(errno)( is | EACCES = PermissionDenied, is | ENOENT = NotFound, diff --git a/src/util/types.hpp b/src/util/types.hpp index 2490dc7..c4c0f19 100644 --- a/src/util/types.hpp +++ b/src/util/types.hpp @@ -11,8 +11,6 @@ #include // std::pair (Pair) #include // std::vector (Vec) -#include "include/matchit.h" - namespace util::types { using u8 = std::uint8_t; ///< 8-bit unsigned integer. using u16 = std::uint16_t; ///< 16-bit unsigned integer.