fix
This commit is contained in:
196
kernel/src/filesystem.rs
Normal file
196
kernel/src/filesystem.rs
Normal file
@ -0,0 +1,196 @@
|
||||
//! Simple File System for Aios
|
||||
//!
|
||||
//! Provides basic file operations for package installation and management
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Simple file entry
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileEntry {
|
||||
pub name: [u8; 32],
|
||||
pub size: usize,
|
||||
pub file_type: FileType,
|
||||
pub data_offset: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum FileType {
|
||||
Regular,
|
||||
Directory,
|
||||
Executable,
|
||||
}
|
||||
|
||||
/// Simple in-memory file system
|
||||
pub struct SimpleFS {
|
||||
files: [Option<FileEntry>; 64],
|
||||
file_count: usize,
|
||||
data_storage: [u8; 2048], // 2KB storage (reduced from 8KB)
|
||||
data_used: usize,
|
||||
}
|
||||
|
||||
impl SimpleFS {
|
||||
pub fn new() -> Self {
|
||||
SimpleFS {
|
||||
files: [None; 64],
|
||||
file_count: 0,
|
||||
data_storage: [0; 2048],
|
||||
data_used: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a file
|
||||
pub fn create_file(&mut self, name: &str, content: &[u8], file_type: FileType) -> Result<(), &'static str> {
|
||||
if self.file_count >= 64 {
|
||||
return Err("File system full");
|
||||
}
|
||||
|
||||
if name.len() > 31 {
|
||||
return Err("Filename too long");
|
||||
}
|
||||
|
||||
if self.data_used + content.len() > self.data_storage.len() {
|
||||
return Err("Not enough storage space");
|
||||
}
|
||||
|
||||
// Check if file already exists
|
||||
if self.find_file(name).is_some() {
|
||||
return Err("File already exists");
|
||||
}
|
||||
|
||||
// Create file entry
|
||||
let mut file_name = [0u8; 32];
|
||||
let name_bytes = name.as_bytes();
|
||||
for i in 0..name_bytes.len() {
|
||||
file_name[i] = name_bytes[i];
|
||||
}
|
||||
|
||||
let file_entry = FileEntry {
|
||||
name: file_name,
|
||||
size: content.len(),
|
||||
file_type,
|
||||
data_offset: self.data_used,
|
||||
};
|
||||
|
||||
// Store file data
|
||||
for i in 0..content.len() {
|
||||
self.data_storage[self.data_used + i] = content[i];
|
||||
}
|
||||
self.data_used += content.len();
|
||||
|
||||
// Add file entry
|
||||
self.files[self.file_count] = Some(file_entry);
|
||||
self.file_count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Find a file by name
|
||||
pub fn find_file(&self, name: &str) -> Option<&FileEntry> {
|
||||
for i in 0..self.file_count {
|
||||
if let Some(file) = &self.files[i] {
|
||||
if self.str_eq(&file.name, name) {
|
||||
return Some(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// List all files
|
||||
pub fn list_files(&self) -> &[Option<FileEntry>] {
|
||||
&self.files[..self.file_count]
|
||||
}
|
||||
|
||||
/// Read file content
|
||||
pub fn read_file(&self, name: &str) -> Option<&[u8]> {
|
||||
if let Some(file) = self.find_file(name) {
|
||||
let start = file.data_offset;
|
||||
let end = start + file.size;
|
||||
Some(&self.data_storage[start..end])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete a file
|
||||
pub fn delete_file(&mut self, name: &str) -> Result<(), &'static str> {
|
||||
let mut found_index = None;
|
||||
for i in 0..self.file_count {
|
||||
if let Some(file) = &self.files[i] {
|
||||
if self.str_eq(&file.name, name) {
|
||||
found_index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(index) = found_index {
|
||||
// Remove file by shifting array
|
||||
for i in index..self.file_count.saturating_sub(1) {
|
||||
self.files[i] = self.files[i + 1];
|
||||
}
|
||||
self.files[self.file_count - 1] = None;
|
||||
self.file_count -= 1;
|
||||
Ok(())
|
||||
} else {
|
||||
Err("File not found")
|
||||
}
|
||||
}
|
||||
|
||||
/// Get file count
|
||||
pub fn file_count(&self) -> usize {
|
||||
self.file_count
|
||||
}
|
||||
|
||||
/// Helper function to compare string with byte array
|
||||
fn str_eq(&self, arr: &[u8; 32], s: &str) -> bool {
|
||||
let s_bytes = s.as_bytes();
|
||||
if s_bytes.len() > 32 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the end of the string in the array (null terminator)
|
||||
let mut arr_len = 0;
|
||||
for i in 0..32 {
|
||||
if arr[i] == 0 {
|
||||
break;
|
||||
}
|
||||
arr_len += 1;
|
||||
}
|
||||
|
||||
if arr_len != s_bytes.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..arr_len {
|
||||
if arr[i] != s_bytes[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Get filename as string
|
||||
pub fn filename_str(file: &FileEntry) -> &str {
|
||||
let mut len = 0;
|
||||
for i in 0..32 {
|
||||
if file.name[i] == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
|
||||
core::str::from_utf8(&file.name[..len]).unwrap_or("")
|
||||
}
|
||||
}
|
||||
|
||||
impl FileType {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
FileType::Regular => "file",
|
||||
FileType::Directory => "dir",
|
||||
FileType::Executable => "exec",
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![allow(static_mut_refs)]
|
||||
|
||||
mod keyboard_basic;
|
||||
|
||||
@ -113,7 +114,7 @@ fn execute_command() {
|
||||
}
|
||||
|
||||
// Clear input buffer
|
||||
INPUT_BUFFER.fill(0);
|
||||
core::ptr::write_bytes(INPUT_BUFFER.as_mut_ptr(), 0, INPUT_BUFFER.len());
|
||||
INPUT_POS = 0;
|
||||
|
||||
new_line();
|
||||
@ -196,7 +197,7 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
// Panic handler
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
clear_screen();
|
||||
print_str("!!! KERNEL PANIC !!!", 29, 12);
|
||||
print_str("System halted.", 33, 13);
|
||||
|
380
kernel/src/package.rs
Normal file
380
kernel/src/package.rs
Normal file
@ -0,0 +1,380 @@
|
||||
//! Package Manager for Aios
|
||||
//!
|
||||
//! Simple package management system inspired by Redox pkgutils
|
||||
//! Provides basic package operations: install, remove, list, update
|
||||
|
||||
#![allow(dead_code)]
|
||||
use core::fmt;
|
||||
use crate::filesystem::{SimpleFS, FileType};
|
||||
|
||||
/// Package metadata structure
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Package {
|
||||
pub name: [u8; 32], // Fixed-size name
|
||||
pub version: [u8; 16], // Fixed-size version
|
||||
pub description: [u8; 64], // Fixed-size description
|
||||
pub status: PackageStatus,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
/// Package status
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PackageStatus {
|
||||
Installed,
|
||||
Available,
|
||||
Broken,
|
||||
}
|
||||
|
||||
/// Package manager error types
|
||||
#[derive(Debug)]
|
||||
pub enum PackageError {
|
||||
NotFound,
|
||||
AlreadyInstalled,
|
||||
NotInstalled,
|
||||
InvalidName,
|
||||
IoError,
|
||||
}
|
||||
|
||||
impl fmt::Display for PackageError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
PackageError::NotFound => write!(f, "Package not found"),
|
||||
PackageError::AlreadyInstalled => write!(f, "Package already installed"),
|
||||
PackageError::NotInstalled => write!(f, "Package not installed"),
|
||||
PackageError::InvalidName => write!(f, "Invalid package name"),
|
||||
PackageError::IoError => write!(f, "I/O error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Package Manager
|
||||
pub struct PackageManager {
|
||||
installed_packages: [Option<Package>; 32], // Simple fixed-size array
|
||||
package_count: usize,
|
||||
filesystem: SimpleFS,
|
||||
}
|
||||
|
||||
impl PackageManager {
|
||||
/// Create new package manager
|
||||
pub fn new() -> Self {
|
||||
PackageManager {
|
||||
installed_packages: [None; 32],
|
||||
package_count: 0,
|
||||
filesystem: SimpleFS::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Install a package
|
||||
pub fn install(&mut self, name: &str) -> Result<(), PackageError> {
|
||||
// Check if package name is valid
|
||||
if name.is_empty() || name.len() > 32 {
|
||||
return Err(PackageError::InvalidName);
|
||||
}
|
||||
|
||||
// Check if already installed
|
||||
if self.is_installed(name) {
|
||||
return Err(PackageError::AlreadyInstalled);
|
||||
}
|
||||
|
||||
// Check if we have space
|
||||
if self.package_count >= 32 {
|
||||
return Err(PackageError::IoError);
|
||||
}
|
||||
|
||||
// Create package from predefined list
|
||||
let package = self.create_package(name)?;
|
||||
|
||||
// Install package files to filesystem
|
||||
self.install_package_files(name)?;
|
||||
|
||||
// Add to installed packages
|
||||
self.installed_packages[self.package_count] = Some(package);
|
||||
self.package_count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a package
|
||||
pub fn remove(&mut self, name: &str) -> Result<(), PackageError> {
|
||||
// Find package
|
||||
let mut found_index = None;
|
||||
for (i, pkg) in self.installed_packages.iter().enumerate() {
|
||||
if let Some(package) = pkg {
|
||||
if self.str_eq(&package.name, name) {
|
||||
found_index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match found_index {
|
||||
Some(index) => {
|
||||
// Remove package files from filesystem
|
||||
let _ = self.remove_package_files(name);
|
||||
|
||||
// Remove package by shifting array
|
||||
for i in index..self.package_count.saturating_sub(1) {
|
||||
self.installed_packages[i] = self.installed_packages[i + 1].take();
|
||||
}
|
||||
self.package_count -= 1;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(PackageError::NotInstalled),
|
||||
}
|
||||
}
|
||||
|
||||
/// List installed packages
|
||||
pub fn list(&self) -> &[Option<Package>] {
|
||||
&self.installed_packages[..self.package_count]
|
||||
}
|
||||
|
||||
/// Check if package is installed
|
||||
pub fn is_installed(&self, name: &str) -> bool {
|
||||
self.installed_packages[..self.package_count]
|
||||
.iter()
|
||||
.any(|pkg| {
|
||||
if let Some(package) = pkg {
|
||||
self.str_eq(&package.name, name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get package count
|
||||
pub fn count(&self) -> usize {
|
||||
self.package_count
|
||||
}
|
||||
|
||||
/// Helper function to compare string with byte array
|
||||
fn str_eq(&self, arr: &[u8; 32], s: &str) -> bool {
|
||||
let s_bytes = s.as_bytes();
|
||||
if s_bytes.len() > 32 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the end of the string in the array (null terminator)
|
||||
let mut arr_len = 0;
|
||||
for i in 0..32 {
|
||||
if arr[i] == 0 {
|
||||
break;
|
||||
}
|
||||
arr_len += 1;
|
||||
}
|
||||
|
||||
if arr_len != s_bytes.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..arr_len {
|
||||
if arr[i] != s_bytes[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Copy string to byte array
|
||||
fn copy_str_to_array<const N: usize>(&self, s: &str) -> [u8; N] {
|
||||
let mut arr = [0u8; N];
|
||||
let bytes = s.as_bytes();
|
||||
let len = core::cmp::min(bytes.len(), N - 1); // Leave space for null terminator
|
||||
|
||||
for i in 0..len {
|
||||
arr[i] = bytes[i];
|
||||
}
|
||||
|
||||
arr
|
||||
}
|
||||
|
||||
/// Create a package from predefined list
|
||||
fn create_package(&self, name: &str) -> Result<Package, PackageError> {
|
||||
match name {
|
||||
"vim" => Ok(Package {
|
||||
name: self.copy_str_to_array("vim"),
|
||||
version: self.copy_str_to_array("8.2.0"),
|
||||
description: self.copy_str_to_array("Vi IMproved text editor"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 1024,
|
||||
}),
|
||||
"git" => Ok(Package {
|
||||
name: self.copy_str_to_array("git"),
|
||||
version: self.copy_str_to_array("2.39.0"),
|
||||
description: self.copy_str_to_array("Git version control system"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 2048,
|
||||
}),
|
||||
"curl" => Ok(Package {
|
||||
name: self.copy_str_to_array("curl"),
|
||||
version: self.copy_str_to_array("7.88.0"),
|
||||
description: self.copy_str_to_array("Command line tool for transferring data"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 512,
|
||||
}),
|
||||
"rust" => Ok(Package {
|
||||
name: self.copy_str_to_array("rust"),
|
||||
version: self.copy_str_to_array("1.75.0"),
|
||||
description: self.copy_str_to_array("Rust programming language"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 4096,
|
||||
}),
|
||||
"python" => Ok(Package {
|
||||
name: self.copy_str_to_array("python"),
|
||||
version: self.copy_str_to_array("3.11.0"),
|
||||
description: self.copy_str_to_array("Python programming language"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 3072,
|
||||
}),
|
||||
"claude" => Ok(Package {
|
||||
name: self.copy_str_to_array("claude"),
|
||||
version: self.copy_str_to_array("1.0.0"),
|
||||
description: self.copy_str_to_array("Claude AI integration service"),
|
||||
status: PackageStatus::Installed,
|
||||
size: 1536,
|
||||
}),
|
||||
_ => Err(PackageError::NotFound),
|
||||
}
|
||||
}
|
||||
|
||||
/// Install package files to filesystem
|
||||
fn install_package_files(&mut self, name: &str) -> Result<(), PackageError> {
|
||||
match name {
|
||||
"vim" => {
|
||||
// Create vim executable
|
||||
let vim_content = b"#!/bin/sh\necho 'Vi IMproved - text editor'\necho 'Usage: vim [file]'\n";
|
||||
self.filesystem.create_file("/bin/vim", vim_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
|
||||
// Create vim config
|
||||
let vimrc_content = b"\" Vim configuration\nset number\nset autoindent\nsyntax on\n";
|
||||
self.filesystem.create_file("/etc/vimrc", vimrc_content, FileType::Regular)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
"git" => {
|
||||
let git_content = b"#!/bin/sh\necho 'Git - version control system'\necho 'Usage: git [command]'\n";
|
||||
self.filesystem.create_file("/bin/git", git_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
|
||||
let gitconfig_content = b"[core]\n editor = vim\n[user]\n name = Aios User\n";
|
||||
self.filesystem.create_file("/etc/gitconfig", gitconfig_content, FileType::Regular)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
"curl" => {
|
||||
let curl_content = b"#!/bin/sh\necho 'cURL - data transfer tool'\necho 'Usage: curl [URL]'\n";
|
||||
self.filesystem.create_file("/bin/curl", curl_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
"rust" => {
|
||||
let rustc_content = b"#!/bin/sh\necho 'Rust Compiler'\necho 'Usage: rustc [file.rs]'\n";
|
||||
self.filesystem.create_file("/bin/rustc", rustc_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
|
||||
let cargo_content = b"#!/bin/sh\necho 'Cargo - Rust package manager'\necho 'Usage: cargo [command]'\n";
|
||||
self.filesystem.create_file("/bin/cargo", cargo_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
"python" => {
|
||||
let python_content = b"#!/bin/sh\necho 'Python interpreter'\necho 'Usage: python [script.py]'\n";
|
||||
self.filesystem.create_file("/bin/python", python_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
|
||||
let pip_content = b"#!/bin/sh\necho 'pip - Python package installer'\necho 'Usage: pip install [package]'\n";
|
||||
self.filesystem.create_file("/bin/pip", pip_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
"claude" => {
|
||||
let claude_content = b"#!/bin/sh\necho 'Claude AI Assistant'\necho 'Usage: claude [query]'\n";
|
||||
self.filesystem.create_file("/bin/claude", claude_content, FileType::Executable)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
|
||||
let config_content = b"# Claude AI Configuration\napi_endpoint=https://api.anthropic.com\nmodel=claude-3-sonnet\n";
|
||||
self.filesystem.create_file("/etc/claude.conf", config_content, FileType::Regular)
|
||||
.map_err(|_| PackageError::IoError)?;
|
||||
}
|
||||
_ => return Err(PackageError::NotFound),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove package files from filesystem
|
||||
fn remove_package_files(&mut self, name: &str) -> Result<(), PackageError> {
|
||||
match name {
|
||||
"vim" => {
|
||||
let _ = self.filesystem.delete_file("/bin/vim");
|
||||
let _ = self.filesystem.delete_file("/etc/vimrc");
|
||||
}
|
||||
"git" => {
|
||||
let _ = self.filesystem.delete_file("/bin/git");
|
||||
let _ = self.filesystem.delete_file("/etc/gitconfig");
|
||||
}
|
||||
"curl" => {
|
||||
let _ = self.filesystem.delete_file("/bin/curl");
|
||||
}
|
||||
"rust" => {
|
||||
let _ = self.filesystem.delete_file("/bin/rustc");
|
||||
let _ = self.filesystem.delete_file("/bin/cargo");
|
||||
}
|
||||
"python" => {
|
||||
let _ = self.filesystem.delete_file("/bin/python");
|
||||
let _ = self.filesystem.delete_file("/bin/pip");
|
||||
}
|
||||
"claude" => {
|
||||
let _ = self.filesystem.delete_file("/bin/claude");
|
||||
let _ = self.filesystem.delete_file("/etc/claude.conf");
|
||||
}
|
||||
_ => return Err(PackageError::NotFound),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List installed files
|
||||
pub fn list_files(&self) -> &[Option<crate::filesystem::FileEntry>] {
|
||||
self.filesystem.list_files()
|
||||
}
|
||||
|
||||
/// Get filesystem reference
|
||||
pub fn filesystem(&self) -> &SimpleFS {
|
||||
&self.filesystem
|
||||
}
|
||||
}
|
||||
|
||||
impl Package {
|
||||
/// Get package name as string slice
|
||||
pub fn name_str(&self) -> &str {
|
||||
// Find null terminator
|
||||
let mut len = 0;
|
||||
for i in 0..32 {
|
||||
if self.name[i] == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
|
||||
// Safe to unwrap since we control the input
|
||||
core::str::from_utf8(&self.name[..len]).unwrap_or("")
|
||||
}
|
||||
|
||||
/// Get package version as string slice
|
||||
pub fn version_str(&self) -> &str {
|
||||
let mut len = 0;
|
||||
for i in 0..16 {
|
||||
if self.version[i] == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
|
||||
core::str::from_utf8(&self.version[..len]).unwrap_or("")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PackageStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
PackageStatus::Installed => write!(f, "installed"),
|
||||
PackageStatus::Available => write!(f, "available"),
|
||||
PackageStatus::Broken => write!(f, "broken"),
|
||||
}
|
||||
}
|
||||
}
|
231
kernel/src/simple_package.rs
Normal file
231
kernel/src/simple_package.rs
Normal file
@ -0,0 +1,231 @@
|
||||
//! Lightweight Package Manager for Aios
|
||||
//!
|
||||
//! Memory-safe package management system with minimal memory footprint
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Lightweight package entry
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct LightPackage {
|
||||
pub name: [u8; 16], // Reduced from 32 to 16 bytes
|
||||
pub version: [u8; 8], // Reduced from 16 to 8 bytes
|
||||
pub status: PackageStatus,
|
||||
}
|
||||
|
||||
/// Package status
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum PackageStatus {
|
||||
Installed,
|
||||
Available,
|
||||
}
|
||||
|
||||
/// Lightweight Package Manager
|
||||
pub struct LightPackageManager {
|
||||
packages: [Option<LightPackage>; 8], // Reduced from 32 to 8 packages
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl LightPackageManager {
|
||||
pub fn new() -> Self {
|
||||
LightPackageManager {
|
||||
packages: [None; 8],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Install a package (lightweight - no actual files)
|
||||
pub fn install(&mut self, name: &str) -> Result<(), &'static str> {
|
||||
if name.len() > 15 {
|
||||
return Err("Package name too long");
|
||||
}
|
||||
|
||||
if self.count >= 8 {
|
||||
return Err("Package manager full");
|
||||
}
|
||||
|
||||
// Check if already installed
|
||||
if self.is_installed(name) {
|
||||
return Err("Already installed");
|
||||
}
|
||||
|
||||
// Create package entry
|
||||
let package = self.create_package(name)?;
|
||||
self.packages[self.count] = Some(package);
|
||||
self.count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove a package
|
||||
pub fn remove(&mut self, name: &str) -> Result<(), &'static str> {
|
||||
let mut found_index = None;
|
||||
for (i, pkg) in self.packages.iter().enumerate() {
|
||||
if let Some(package) = pkg {
|
||||
if self.str_eq(&package.name, name) {
|
||||
found_index = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match found_index {
|
||||
Some(index) => {
|
||||
// Remove package by shifting array
|
||||
for i in index..self.count.saturating_sub(1) {
|
||||
self.packages[i] = self.packages[i + 1];
|
||||
}
|
||||
self.packages[self.count - 1] = None;
|
||||
self.count -= 1;
|
||||
Ok(())
|
||||
}
|
||||
None => Err("Package not installed"),
|
||||
}
|
||||
}
|
||||
|
||||
/// List installed packages
|
||||
pub fn list(&self) -> &[Option<LightPackage>] {
|
||||
&self.packages[..self.count]
|
||||
}
|
||||
|
||||
/// Check if package is installed
|
||||
pub fn is_installed(&self, name: &str) -> bool {
|
||||
self.packages[..self.count]
|
||||
.iter()
|
||||
.any(|pkg| {
|
||||
if let Some(package) = pkg {
|
||||
self.str_eq(&package.name, name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get package count
|
||||
pub fn count(&self) -> usize {
|
||||
self.count
|
||||
}
|
||||
|
||||
/// Create a package from predefined list
|
||||
fn create_package(&self, name: &str) -> Result<LightPackage, &'static str> {
|
||||
match name {
|
||||
"vim" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("vim"),
|
||||
version: self.copy_str_to_array::<8>("8.2"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"git" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("git"),
|
||||
version: self.copy_str_to_array::<8>("2.39"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"curl" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("curl"),
|
||||
version: self.copy_str_to_array::<8>("7.88"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"rust" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("rust"),
|
||||
version: self.copy_str_to_array::<8>("1.75"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"python" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("python"),
|
||||
version: self.copy_str_to_array::<8>("3.11"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"claude" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("claude"),
|
||||
version: self.copy_str_to_array::<8>("1.0"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"zsh" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("zsh"),
|
||||
version: self.copy_str_to_array::<8>("5.9"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"bash" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("bash"),
|
||||
version: self.copy_str_to_array::<8>("5.2"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
"fish" => Ok(LightPackage {
|
||||
name: self.copy_str_to_array::<16>("fish"),
|
||||
version: self.copy_str_to_array::<8>("3.6"),
|
||||
status: PackageStatus::Installed,
|
||||
}),
|
||||
_ => Err("Package not found"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to compare string with byte array
|
||||
fn str_eq(&self, arr: &[u8; 16], s: &str) -> bool {
|
||||
let s_bytes = s.as_bytes();
|
||||
if s_bytes.len() > 16 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the end of the string in the array (null terminator)
|
||||
let mut arr_len = 0;
|
||||
for i in 0..16 {
|
||||
if arr[i] == 0 {
|
||||
break;
|
||||
}
|
||||
arr_len += 1;
|
||||
}
|
||||
|
||||
if arr_len != s_bytes.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..arr_len {
|
||||
if arr[i] != s_bytes[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Copy string to byte array
|
||||
fn copy_str_to_array<const N: usize>(&self, s: &str) -> [u8; N] {
|
||||
let mut arr = [0u8; N];
|
||||
let bytes = s.as_bytes();
|
||||
let len = core::cmp::min(bytes.len(), N - 1); // Leave space for null terminator
|
||||
|
||||
for i in 0..len {
|
||||
arr[i] = bytes[i];
|
||||
}
|
||||
|
||||
arr
|
||||
}
|
||||
}
|
||||
|
||||
impl LightPackage {
|
||||
/// Get package name as string slice
|
||||
pub fn name_str(&self) -> &str {
|
||||
// Find null terminator
|
||||
let mut len = 0;
|
||||
for i in 0..16 {
|
||||
if self.name[i] == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
|
||||
// Safe to unwrap since we control the input
|
||||
core::str::from_utf8(&self.name[..len]).unwrap_or("")
|
||||
}
|
||||
|
||||
/// Get package version as string slice
|
||||
pub fn version_str(&self) -> &str {
|
||||
let mut len = 0;
|
||||
for i in 0..8 {
|
||||
if self.version[i] == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
|
||||
core::str::from_utf8(&self.version[..len]).unwrap_or("")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user