minilink 0.2.0

Template and register linker scripts
Documentation
use std::collections::HashMap;

use serde::{Serialize, Serializer, ser::SerializeStruct};

pub(crate) struct LinkerTemplateContext {
    cfg: HashMap<String, TemplateContextCfg>,
}

impl LinkerTemplateContext {
    pub(crate) fn new() -> Self {
        let cfg = std::env::vars()
            .filter_map(|(name, value)| {
                name.strip_prefix("CARGO_CFG_")
                    .map(|name| (name.to_lowercase(), TemplateContextCfg::from_env_value(value)))
            })
            .collect();

        Self { cfg }
    }
}

impl Serialize for LinkerTemplateContext {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut struct_serializer = serializer.serialize_struct("LinkerTemplateContext", 1)?;
        struct_serializer.serialize_field("cfg", &self.cfg)?;
        struct_serializer.end()
    }
}

pub(crate) enum TemplateContextCfg {
    Value(String),
    List(Vec<String>),
}

impl TemplateContextCfg {
    pub(crate) fn from_env_value(str_value: String) -> Self {
        if str_value.contains(',') {
            let values = str_value.split(',').map(ToOwned::to_owned).collect();
            TemplateContextCfg::List(values)
        } else {
            TemplateContextCfg::Value(str_value)
        }
    }
}

impl Serialize for TemplateContextCfg {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            TemplateContextCfg::Value(value) => value.serialize(serializer),
            TemplateContextCfg::List(list) => list.serialize(serializer),
        }
    }
}

impl<'de> serde::Deserialize<'de> for TemplateContextCfg {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_any(TemplateContextCfgVisitor)
    }
}

struct TemplateContextCfgVisitor;

impl<'de> serde::de::Visitor<'de> for TemplateContextCfgVisitor {
    type Value = TemplateContextCfg;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(formatter, "a singular string or list of strings")
    }

    fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
        let mut buffer = Vec::with_capacity(seq.size_hint().unwrap_or_default());

        while let Some(element) = seq.next_element()? {
            buffer.push(element);
        }

        Ok(TemplateContextCfg::List(buffer))
    }

    fn visit_str<E: serde::de::Error>(self, str: &str) -> Result<Self::Value, E> {
        Ok(TemplateContextCfg::Value(str.to_owned()))
    }
}