feat(k-ui): first slice: App trait + run loop, builders (column/row/text/button), flex layout, theme tokens (Whimsy Dark), tiny-skia + fontdue render (Noto Sans Latin subset), keyboard focus ring; tally demo; manifest memory key (pm spawns with it)
This commit is contained in:
@@ -144,3 +144,32 @@ impl Accent {
|
||||
u32::from_str_radix(hex, 16).ok().map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// How much memory an app may use, in 4 KiB pages: written `"512 KiB"` or
|
||||
/// `"3 MiB"`, from 64 KiB to 16 MiB.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Memory(u16);
|
||||
|
||||
impl Memory {
|
||||
/// What an app gets unless its manifest says more: enough for a small
|
||||
/// command-line program.
|
||||
pub const DEFAULT: Self = Self(128);
|
||||
const MIN_PAGES: u32 = 16;
|
||||
const MAX_PAGES: u32 = 4096;
|
||||
|
||||
pub fn parse(s: &str) -> Option<Self> {
|
||||
let (n, unit) = s.split_once(' ')?;
|
||||
let n: u32 = n.parse().ok()?;
|
||||
let kib = match unit {
|
||||
"KiB" => n,
|
||||
"MiB" => n.checked_mul(1024)?,
|
||||
_ => return None,
|
||||
};
|
||||
let pages = kib.div_ceil(4);
|
||||
(kib % 4 == 0 && (Self::MIN_PAGES..=Self::MAX_PAGES).contains(&pages)).then_some(Self(pages as u16))
|
||||
}
|
||||
|
||||
pub fn pages(&self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ mod tests;
|
||||
|
||||
pub use capability::{Capabilities, Capability};
|
||||
pub use decision::{Decision, MAX_TEXT as MAX_DECISION_TEXT};
|
||||
pub use fields::{Accent, AppId, Command, FileName, Text, Version};
|
||||
pub use fields::{Accent, AppId, Command, FileName, Memory, Text, Version};
|
||||
pub use manifest::Manifest;
|
||||
|
||||
/// Why a manifest or decision was refused; `line` is 1-based.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::Error;
|
||||
use crate::capability::{Capabilities, Capability};
|
||||
use crate::fields::{Accent, AppId, Command, FileName, Text, Version};
|
||||
use crate::fields::{Accent, AppId, Command, FileName, Memory, Text, Version};
|
||||
use crate::toml::{self, Entry, Value};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -15,6 +15,8 @@ pub struct Manifest {
|
||||
pub command: Command,
|
||||
pub author: Option<Text<64>>,
|
||||
pub icon: Option<FileName>,
|
||||
/// How much memory it may use.
|
||||
pub memory: Memory,
|
||||
/// Denied = not installed.
|
||||
pub required: Capabilities,
|
||||
/// The owner decides each.
|
||||
@@ -32,6 +34,7 @@ struct Draft {
|
||||
command: Option<Command>,
|
||||
author: Option<Text<64>>,
|
||||
icon: Option<FileName>,
|
||||
memory: Option<Memory>,
|
||||
required: Option<Capabilities>,
|
||||
optional: Option<Capabilities>,
|
||||
accent: Option<Accent>,
|
||||
@@ -81,6 +84,7 @@ impl Draft {
|
||||
("app", "command") => set(&mut self.command, s.and_then(Command::new), l),
|
||||
("app", "author") => set(&mut self.author, s.and_then(Text::new), l),
|
||||
("app", "icon") => set(&mut self.icon, s.and_then(FileName::new), l),
|
||||
("app", "memory") => set(&mut self.memory, s.and_then(Memory::parse), l),
|
||||
("capabilities", "required") => {
|
||||
set(&mut self.required, Some(capabilities(e.value, l)?), l)
|
||||
}
|
||||
@@ -116,6 +120,7 @@ impl Manifest {
|
||||
command,
|
||||
author: d.author,
|
||||
icon: d.icon,
|
||||
memory: d.memory.unwrap_or(Memory::DEFAULT),
|
||||
required,
|
||||
optional,
|
||||
accent: d.accent,
|
||||
|
||||
@@ -239,3 +239,13 @@ fn every_capability_name_round_trips_and_fits_the_set() {
|
||||
all
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memory_is_in_kib_or_mib_within_bounds() {
|
||||
use crate::Memory;
|
||||
assert_eq!(Memory::parse("3 MiB").map(|m| m.pages()), Some(768));
|
||||
assert_eq!(Memory::parse("512 KiB").map(|m| m.pages()), Some(128));
|
||||
for bad in ["3MiB", "3 GiB", "17 MiB", "32 KiB", "6 KiB", "-1 MiB", "x MiB"] {
|
||||
assert_eq!(Memory::parse(bad), None, "{bad}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user