use std::collections::{BTreeMap, BTreeSet};
use anyhow::Context as _;
use serde::{Deserialize, Serialize};
use crate::{CrateName, Symbol, cap_rule::SymbolRules, symbol::FunctionOrPath};
pub type CapabilitySet = BTreeSet<Capability>;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Capability {
#[serde(rename = "build.rs")]
BuildRs,
#[serde(rename = "alloc")]
Alloc,
#[serde(rename = "panic")]
Panic,
#[serde(rename = "time")]
Time,
#[serde(rename = "sysinfo")]
Sysinfo,
#[serde(rename = "stdio")]
Stdio,
#[serde(rename = "thread")]
Thread,
#[serde(rename = "net")]
Net,
#[serde(rename = "fs")]
FS,
#[serde(rename = "*")]
Any,
}
impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BuildRs => write!(f, "build.rs"),
Self::Alloc => write!(f, "alloc"),
Self::Panic => write!(f, "panic"),
Self::Time => write!(f, "time"),
Self::Sysinfo => write!(f, "sysinfo"),
Self::Stdio => write!(f, "stdio"),
Self::Thread => write!(f, "thread"),
Self::Net => write!(f, "net"),
Self::FS => write!(f, "fs"),
Self::Any => write!(f, "any"),
}
}
}
impl Capability {
pub fn emoji(&self) -> &'static str {
match self {
Self::BuildRs => "๐ ๏ธ ",
Self::Alloc => "๐ฆ",
Self::Panic => "โ๏ธ",
Self::Time => "โฐ",
Self::Sysinfo => "๐ฅ๏ธ ",
Self::Stdio => "๐",
Self::Thread => "๐งต",
Self::Net => "๐",
Self::FS => "๐",
Self::Any => "โ ๏ธ ",
}
}
}
#[derive(Clone, Debug, Default)]
pub struct DeducedCapabilities {
pub own_caps: BTreeMap<Capability, Reasons>,
pub known_crates: BTreeMap<CrateName, CapabilitySet>,
pub unknown_symbols: BTreeSet<Symbol>,
pub unknown_crates: BTreeMap<CrateName, BTreeSet<Symbol>>,
}
pub type Reasons = BTreeSet<Reason>;
pub type Reason = Symbol;
impl DeducedCapabilities {
pub fn from_symbols(
rules: &SymbolRules,
symbols: impl IntoIterator<Item = Symbol>,
) -> anyhow::Result<Self> {
let mut slf = Self::default();
for symbol in symbols {
slf.add(rules, &symbol)?;
}
Ok(slf)
}
pub fn total_capabilities(&self) -> CapabilitySet {
let Self {
own_caps,
known_crates,
unknown_symbols,
unknown_crates,
} = self;
let mut total = BTreeSet::default();
for cap in own_caps.keys() {
total.insert(*cap);
}
for caps in known_crates.values() {
for &cap in caps {
total.insert(cap);
}
}
if !unknown_symbols.is_empty() || !unknown_crates.is_empty() {
total.insert(Capability::Any);
}
if total.contains(&Capability::Any) {
return std::iter::once(Capability::Any).collect();
}
total
}
fn add(&mut self, rules: &SymbolRules, symbol: &Symbol) -> anyhow::Result<()> {
for path in symbol.paths() {
match path {
FunctionOrPath::Function(fun_name) => {
let fun_name = fun_name.trim_start_matches('_');
if let Some(capabilities) = rules.match_symbol(fun_name) {
for &capability in capabilities {
self.own_caps
.entry(capability)
.or_default()
.insert(symbol.clone());
}
} else {
self.unknown_symbols.insert(symbol.clone());
}
}
FunctionOrPath::RustPath(rust_path) => {
let path_str = rust_path.to_string();
if let Some(capabilities) = rules.match_symbol(&path_str) {
for &capability in capabilities {
self.own_caps
.entry(capability)
.or_default()
.insert(symbol.clone());
}
} else {
let segments = rust_path.segments();
let crate_name = segments[0];
let crate_name = CrateName::new(crate_name)
.with_context(|| format!("mangled: {:?}", symbol.mangled))
.with_context(|| format!("demangled: {:?}", symbol.demangled))?;
self.unknown_crates
.entry(crate_name)
.or_default()
.insert(symbol.clone());
}
}
}
}
Ok(())
}
}