nyi: animated 3d fetch
This commit is contained in:
8 files changed
+5425
No files matched your search
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
nyi
|
||||||
|
/target
|
||||||
|
result
|
||||||
|
/dist
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.189"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nyi"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
Executable
+24
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "nyi"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.74"
|
||||||
|
description = "Animated 3D fetch with cool features"
|
||||||
|
homepage = "https://github.com/elwrcl/nyi"
|
||||||
|
repository = "https://github.com/elwrcl/nyi"
|
||||||
|
license = "ISC"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nyi"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
panic = "abort"
|
||||||
|
strip = true
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2026 elwrcl
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// very bad pyramid
|
||||||
|
|
||||||
|
use crate::eq_ci;
|
||||||
|
const LOGO_TRIANGLE: &[&str] = &[
|
||||||
|
"\x1b[92m M",
|
||||||
|
"\x1b[92m NNMNN",
|
||||||
|
"\x1b[92m mmNNMNNmm",
|
||||||
|
"\x1b[93m dmmNNMNNmmd",
|
||||||
|
"\x1b[93m hddmmNNMNNmmddh",
|
||||||
|
"\x1b[93m yhhddmmNNMNNmmddhhy",
|
||||||
|
"\x1b[91m yyhhddmmNNMNNmmddhhyy",
|
||||||
|
"\x1b[91m ooyyhhddmmNNMNNmmddhhyyoo",
|
||||||
|
"\x1b[91m sooyyhhddmmNNMNNmmddhhyyoos",
|
||||||
|
"\x1b[31m =ssooyyhhddmmNNMNNmmddhhyyooss=",
|
||||||
|
"\x1b[31m ===ssooyyhhddmmNNMNNmmddhhyyooss===",
|
||||||
|
"\x1b[31m :===ssooyyhhddmmNNMNNmmddhhyyooss===:",
|
||||||
|
"\x1b[95m -::===ssooyyhhddmmNNMNNmmddhhyyooss===::-",
|
||||||
|
"\x1b[95m --::===ssooyyhhddmmNNMNNmmddhhyyooss===::--",
|
||||||
|
"\x1b[95m //--::===ssooyyhhddmmNNMNNmmddhhyyooss===::--//",
|
||||||
|
"\x1b[95m .///--::===ssooyyhhddmmNNMNNmmddhhyyooss===::--///.",
|
||||||
|
"\x1b[94m ..///--::===ssooyyhhddmmNNMNNmmddhhyyooss===::--///..",
|
||||||
|
"\x1b[94m ``..///--::===ssooyyhhddmmNNMNNmmddhhyyooss===::--///..``",
|
||||||
|
"\x1b[94m```..///--::===ssooyyhhddmmNNMNNmmddhhyyooss===::--///..```",
|
||||||
|
];
|
||||||
|
|
||||||
|
pub struct BuiltinLogo {
|
||||||
|
pub names: &'static [&'static str],
|
||||||
|
pub rows: &'static [&'static str],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const BUILTIN_LOGOS: &[BuiltinLogo] = &[BuiltinLogo {
|
||||||
|
names: &["triangle", "pyramid", "nyi"],
|
||||||
|
rows: LOGO_TRIANGLE,
|
||||||
|
}];
|
||||||
|
|
||||||
|
pub fn builtin_logo(name: &str) -> Option<Vec<String>> {
|
||||||
|
BUILTIN_LOGOS
|
||||||
|
.iter()
|
||||||
|
.find(|l| l.names.iter().any(|n| eq_ci(n, name)))
|
||||||
|
.map(|l| l.rows.iter().map(|s| s.to_string()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DEFAULT_NAME: &str = "triangle";
|
||||||
|
pub fn default_logo() -> Vec<String> {
|
||||||
|
LOGO_TRIANGLE.iter().map(|s| s.to_string()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::{visible_width, Logo};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_logo_is_reachable_by_name() {
|
||||||
|
assert_eq!(builtin_logo(DEFAULT_NAME), Some(default_logo()));
|
||||||
|
assert!(builtin_logo("no-such-thing").is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn names_do_not_collide() {
|
||||||
|
let mut seen: Vec<&str> = Vec::new();
|
||||||
|
for l in BUILTIN_LOGOS {
|
||||||
|
for n in l.names {
|
||||||
|
assert!(!seen.contains(n), "{n} is listed twice");
|
||||||
|
seen.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyramid_fits_the_canvas() {
|
||||||
|
let rows = default_logo();
|
||||||
|
assert!(rows.len() >= 8);
|
||||||
|
assert!(rows.iter().all(|r| visible_width(r) <= 60));
|
||||||
|
let widths: Vec<usize> = rows.iter().map(|r| visible_width(r)).collect();
|
||||||
|
assert_eq!(*widths.last().unwrap(), *widths.iter().max().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pyramid_carries_its_own_colors() {
|
||||||
|
let logo = Logo::process(&default_logo());
|
||||||
|
assert!(logo.has_ansi, "the escapes did not survive parsing");
|
||||||
|
let last = logo.rows - 1;
|
||||||
|
assert!(logo.weight_at(last, logo.cols / 2) > logo.weight_at(last, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
+5186
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,93 @@
|
|||||||
|
// for mobile eg; termux, i$h, newterm.
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
use crate::{contains_ci, count_file_lines, is_shell_safe, run, strip_eol, uname};
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn is_android() -> bool {
|
||||||
|
static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||||
|
*CACHE.get_or_init(|| {
|
||||||
|
cfg!(target_os = "android")
|
||||||
|
|| std::env::var_os("TERMUX_VERSION").is_some()
|
||||||
|
|| fs::metadata("/system/build.prop").is_ok()
|
||||||
|
|| std::env::var("PREFIX").is_ok_and(|p| p.contains("com.termux"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn is_ish() -> bool {
|
||||||
|
static CACHE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
|
||||||
|
*CACHE.get_or_init(|| fs::metadata("/proc/ish").is_ok() || uname().release.contains("ish"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// android `getprop` is on PATH under termux.
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
fn getprop(key: &str) -> Option<String> {
|
||||||
|
if !is_shell_safe(key) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let out = run(&format!("getprop {key} 2>/dev/null"))?;
|
||||||
|
let v = strip_eol(out.split('\n').next()?).trim().to_string();
|
||||||
|
if v.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// android has no /etc/os-release; the version lives in the properties.
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn os_name() -> String {
|
||||||
|
match (
|
||||||
|
getprop("ro.build.version.release"),
|
||||||
|
getprop("ro.build.version.sdk"),
|
||||||
|
) {
|
||||||
|
(Some(r), Some(s)) => format!("Android {r} (API {s})"),
|
||||||
|
(Some(r), None) => format!("Android {r}"),
|
||||||
|
_ => "Android".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn host() -> Option<String> {
|
||||||
|
let brand = getprop("ro.product.manufacturer").or_else(|| getprop("ro.product.brand"));
|
||||||
|
let model = getprop("ro.product.model")?;
|
||||||
|
Some(match brand {
|
||||||
|
Some(b) if !contains_ci(&model, &b) => format!("{b} {model}"),
|
||||||
|
_ => model,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// termux keeps the dpkg database under $PREFIX, not /var/lib.
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
pub fn package_count() -> Option<String> {
|
||||||
|
let prefix = std::env::var("PREFIX").ok()?;
|
||||||
|
let n = count_file_lines(&format!("{prefix}/var/lib/dpkg/status"), Some("Package:"));
|
||||||
|
if n > 0 {
|
||||||
|
Some(format!("{n} (dpkg)"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// walking the process tree finds nothing here termux is an 'seperate' app.
|
||||||
|
pub fn terminal() -> Option<String> {
|
||||||
|
let v = std::env::var("TERMUX_VERSION").ok()?;
|
||||||
|
if v.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(format!("Termux {v}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn logo_override() -> Option<&'static str> {
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
{
|
||||||
|
if is_ish() {
|
||||||
|
return Some("apple");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
Reference in new issue
Block a user