mod export;
pub mod flow_type;
mod impls;
pub use flowjs_rs_macros::Flow;
pub use crate::export::ExportError;
use std::any::TypeId;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct Config {
export_dir: PathBuf,
array_tuple_limit: usize,
}
impl Config {
pub fn new() -> Self {
Self {
export_dir: PathBuf::from("./bindings"),
array_tuple_limit: 64,
}
}
pub fn from_env() -> Self {
let mut cfg = Self::new();
if let Ok(dir) = std::env::var("FLOW_RS_EXPORT_DIR") {
cfg = cfg.with_out_dir(dir);
}
cfg
}
pub fn with_out_dir(mut self, dir: impl Into<PathBuf>) -> Self {
self.export_dir = dir.into();
self
}
pub fn out_dir(&self) -> &Path {
&self.export_dir
}
pub fn with_array_tuple_limit(mut self, limit: usize) -> Self {
self.array_tuple_limit = limit;
self
}
pub fn array_tuple_limit(&self) -> usize {
self.array_tuple_limit
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
pub trait TypeVisitor: Sized {
fn visit<T: Flow + 'static + ?Sized>(&mut self);
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Dependency {
pub type_id: TypeId,
pub flow_name: String,
pub output_path: PathBuf,
}
impl Dependency {
pub fn from_ty<T: Flow + 'static + ?Sized>(cfg: &Config) -> Option<Self> {
let output_path = <T as crate::Flow>::output_path()?;
Some(Dependency {
type_id: TypeId::of::<T>(),
flow_name: <T as crate::Flow>::ident(cfg),
output_path,
})
}
}
pub trait Flow {
type WithoutGenerics: Flow + ?Sized;
type OptionInnerType: ?Sized;
#[doc(hidden)]
const IS_OPTION: bool = false;
const IS_ENUM: bool = false;
fn docs() -> Option<String> {
None
}
fn ident(cfg: &Config) -> String {
let name = <Self as crate::Flow>::name(cfg);
match name.find('<') {
Some(i) => name[..i].to_owned(),
None => name,
}
}
fn decl(cfg: &Config) -> String {
panic!("{} cannot be declared", Self::name(cfg))
}
fn decl_concrete(cfg: &Config) -> String {
panic!("{} cannot be declared", Self::name(cfg))
}
fn name(cfg: &Config) -> String;
fn inline(cfg: &Config) -> String;
fn inline_flattened(cfg: &Config) -> String {
panic!("{} cannot be flattened", Self::name(cfg))
}
fn visit_dependencies(_: &mut impl TypeVisitor)
where
Self: 'static,
{
}
fn visit_generics(_: &mut impl TypeVisitor)
where
Self: 'static,
{
}
fn dependencies(cfg: &Config) -> Vec<Dependency>
where
Self: 'static,
{
struct Visit<'a>(&'a Config, &'a mut Vec<Dependency>);
impl TypeVisitor for Visit<'_> {
fn visit<T: Flow + 'static + ?Sized>(&mut self) {
let Visit(cfg, deps) = self;
if let Some(dep) = Dependency::from_ty::<T>(cfg) {
deps.push(dep);
}
}
}
let mut deps: Vec<Dependency> = vec![];
Self::visit_dependencies(&mut Visit(cfg, &mut deps));
deps
}
fn output_path() -> Option<PathBuf> {
None
}
fn export(cfg: &Config) -> Result<(), ExportError>
where
Self: 'static,
{
let relative = Self::output_path()
.ok_or(ExportError::CannotBeExported(std::any::type_name::<Self>()))?;
let path = cfg.export_dir.join(relative);
export::export_to::<Self>(cfg, &path)
}
fn export_all(cfg: &Config) -> Result<(), ExportError>
where
Self: 'static,
{
export::export_all_into::<Self>(cfg)
}
fn export_to_string(cfg: &Config) -> Result<String, ExportError>
where
Self: 'static,
{
export::export_to_string::<Self>(cfg)
}
}
pub struct Dummy;
impl Flow for Dummy {
type WithoutGenerics = Self;
type OptionInnerType = Self;
fn name(_: &Config) -> String {
flow_type::ANY.to_owned()
}
fn inline(_: &Config) -> String {
flow_type::ANY.to_owned()
}
}