use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::{
ffi::OsString,
os::unix::ffi::{OsStrExt, OsStringExt},
};
use sim_kernel::{CatalogSource, Datum, Error, LibSource, LibSourceSpec, Result, Symbol};
pub const PATH_SOURCE_KIND: &str = "path";
pub const URL_SOURCE_KIND: &str = "url";
pub const BYTES_SOURCE_KIND: &str = "bytes";
pub const CONTENT_ADDRESS_SOURCE_KIND: &str = "content-address";
pub fn path_source_kind() -> Symbol {
Symbol::new(PATH_SOURCE_KIND)
}
pub fn url_source_kind() -> Symbol {
Symbol::new(URL_SOURCE_KIND)
}
pub fn bytes_source_kind() -> Symbol {
Symbol::new(BYTES_SOURCE_KIND)
}
pub fn content_address_source_kind() -> Symbol {
Symbol::new(CONTENT_ADDRESS_SOURCE_KIND)
}
pub fn path_source(path: impl Into<PathBuf>) -> LibSource {
LibSource::open(path_source_kind(), path_payload(path.into()))
}
pub fn path_source_spec(path: impl Into<PathBuf>) -> LibSourceSpec {
LibSourceSpec::open(path_source_kind(), path_payload(path.into()))
}
pub fn catalog_path_source(path: impl Into<PathBuf>) -> CatalogSource {
CatalogSource::open(path_source_kind(), path_payload(path.into()))
}
pub fn url_source(url: impl Into<String>) -> LibSource {
LibSource::open(url_source_kind(), Datum::String(url.into()))
}
pub fn url_source_spec(url: impl Into<String>) -> LibSourceSpec {
LibSourceSpec::open(url_source_kind(), Datum::String(url.into()))
}
pub fn catalog_url_source(url: impl Into<String>) -> CatalogSource {
CatalogSource::open(url_source_kind(), Datum::String(url.into()))
}
pub fn bytes_source(bytes: impl Into<Vec<u8>>) -> LibSource {
LibSource::open(bytes_source_kind(), Datum::Bytes(bytes.into()))
}
pub fn bytes_source_spec(bytes: impl Into<Vec<u8>>) -> LibSourceSpec {
LibSourceSpec::open(bytes_source_kind(), Datum::Bytes(bytes.into()))
}
pub fn catalog_bytes_source(bytes: impl Into<Vec<u8>>) -> CatalogSource {
CatalogSource::open(bytes_source_kind(), Datum::Bytes(bytes.into()))
}
pub fn content_address_source(payload: Datum) -> LibSource {
LibSource::open(content_address_source_kind(), payload)
}
pub fn content_address_source_spec(payload: Datum) -> LibSourceSpec {
LibSourceSpec::open(content_address_source_kind(), payload)
}
pub fn catalog_content_address_source(payload: Datum) -> CatalogSource {
CatalogSource::open(content_address_source_kind(), payload)
}
pub fn is_path_source(source: &LibSource) -> bool {
open_payload(source, &path_source_kind()).is_some()
}
pub fn is_url_source(source: &LibSource) -> bool {
open_payload(source, &url_source_kind()).is_some()
}
pub fn is_bytes_source(source: &LibSource) -> bool {
open_payload(source, &bytes_source_kind()).is_some()
}
pub fn path_from_source(source: &LibSource) -> Result<Option<PathBuf>> {
open_payload(source, &path_source_kind())
.map(path_from_payload)
.transpose()
}
pub fn url_from_source(source: &LibSource) -> Result<Option<String>> {
open_payload(source, &url_source_kind())
.map(url_from_payload)
.transpose()
}
pub fn bytes_from_source(source: &LibSource) -> Result<Option<Vec<u8>>> {
open_payload(source, &bytes_source_kind())
.map(bytes_from_payload)
.transpose()
}
pub fn content_address_payload(source: &LibSource) -> Option<&Datum> {
open_payload(source, &content_address_source_kind())
}
pub fn path_payload(path: impl AsRef<Path>) -> Datum {
#[cfg(unix)]
{
Datum::Bytes(path.as_ref().as_os_str().as_bytes().to_vec())
}
#[cfg(not(unix))]
{
Datum::String(path.as_ref().to_string_lossy().into_owned())
}
}
pub fn path_from_payload(payload: &Datum) -> Result<PathBuf> {
match payload {
Datum::String(path) => Ok(PathBuf::from(path)),
#[cfg(unix)]
Datum::Bytes(bytes) => Ok(PathBuf::from(OsString::from_vec(bytes.clone()))),
#[cfg(not(unix))]
Datum::Bytes(_) => Err(Error::HostError(
"path source byte payloads are only supported on unix".to_owned(),
)),
other => Err(Error::TypeMismatch {
expected: "path source string or bytes",
found: datum_kind(other),
}),
}
}
pub fn url_from_payload(payload: &Datum) -> Result<String> {
string_payload(payload)
}
pub fn bytes_from_payload(payload: &Datum) -> Result<Vec<u8>> {
bytes_payload(payload)
}
fn open_payload<'a>(source: &'a LibSource, expected_kind: &Symbol) -> Option<&'a Datum> {
match source {
LibSource::Open { kind, payload } if kind == expected_kind => Some(payload),
_ => None,
}
}
fn string_payload(payload: &Datum) -> Result<String> {
match payload {
Datum::String(value) => Ok(value.clone()),
other => Err(Error::TypeMismatch {
expected: "source string",
found: datum_kind(other),
}),
}
}
fn bytes_payload(payload: &Datum) -> Result<Vec<u8>> {
match payload {
Datum::Bytes(value) => Ok(value.clone()),
other => Err(Error::TypeMismatch {
expected: "source bytes",
found: datum_kind(other),
}),
}
}
fn datum_kind(datum: &Datum) -> &'static str {
match datum {
Datum::Nil => "datum nil",
Datum::Bool(_) => "datum bool",
Datum::Number(_) => "datum number",
Datum::Symbol(_) => "datum symbol",
Datum::String(_) => "datum string",
Datum::Bytes(_) => "datum bytes",
Datum::List(_) => "datum list",
Datum::Vector(_) => "datum vector",
Datum::Map(_) => "datum map",
Datum::Set(_) => "datum set",
Datum::Node { .. } => "datum node",
}
}