audunhalland / entrait Goto Github PK
View Code? Open in Web Editor NEWLoosely coupled Rust application design made easy
Loosely coupled Rust application design made easy
I'm really intrigued by this implementation of dependency injection in Rust! One issue I was running into was how to create a trait with multiple methods.
I tried this
#[entrait]
pub trait Environment {
fn read_var(&self, var: &str) -> Result<String> {
todo!()
}
fn read_var_maybe(&self, var: &str) -> Option<String> {
todo!()
}
}
but then I get an error about entrait::Impl
not implementing Environment
, when I try to use the app
in my code.
This is the relevant generated code:
pub trait Environment {
fn read_var(&self, var: &str) -> Result<String>;
fn read_var_maybe(&self, var: &str) -> Option<String>;
}
impl<EntraitT: Sync> Environment for ::entrait::Impl<EntraitT>
where
EntraitT: Environment + Sync,
{
#[inline]
fn read_var(&self, var: &str) -> Result<String> {
self.as_ref().read_var(var)
}
#[inline]
fn read_var_maybe(&self, var: &str) -> Option<String> {
self.as_ref().read_var_maybe(var)
}
}
I can get it working if I remove the entrait
macro and modify it to this:
pub trait Environment {
fn read_var(&self, var: &str) -> Result<String>;
fn read_var_maybe(&self, var: &str) -> Option<String>;
}
fn read_var(_deps: &impl std::any::Any, var: &str) -> Result<String> {
unimplemented!()
}
fn read_var_maybe(_deps: &impl std::any::Any, var: &str) -> Option<String> {
unimplemented!()
}
impl<EntraitT: Sync> Environment for ::entrait::Impl<EntraitT>
where
EntraitT: Sync,
{
#[inline]
fn read_var(&self, var: &str) -> Result<String> {
read_var(self, var)
}
#[inline]
fn read_var_maybe(&self, var: &str) -> Option<String> {
read_var_maybe(self, var)
}
}
I have to remove the Environment
trait bound and change how read_var
and read_var_maybe
are called. I could definitely be doing something wrong, but it seems like the generated output doesn't compile for traits with multiple methods. Do you have any insights here? If it doesn't work yet, is this a use-case you'd be willing to support?
Cloned main and tried to compile the axum example but was met with (r1.65.0):
error[E0433]: failed to resolve: could not find `__async_trait` in `entrait`
--> examples/axum/src/main.rs:14:36
|
14 | #[entrait(pub GetFoo, no_deps, box_future, mock_api=GetFooMock)]
| ^^^^^^^^^^ could not find `__async_trait` in `entrait`
error[E0706]: functions in traits cannot be declared `async`
--> examples/axum/src/main.rs:14:5
|
14 | #[entrait(pub GetFoo, no_deps, box_future, mock_api=GetFooMock)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15 | async fn get_foo() -> Foo {
| ----- `async` because of this
|
= note: `async` trait functions are not currently supported
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
= note: this error originates in the attribute macro `entrait` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0706]: functions in traits cannot be declared `async`
--> examples/axum/src/main.rs:15:5
|
15 | async fn get_foo() -> Foo {
| -----^^^^^^^^^^^^^^^^^^^^
| |
| `async` because of this
|
= note: `async` trait functions are not currently supported
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
error[E0277]: the trait bound `fn(Extension<A>) -> impl Future<Output = Json<Foo>> {Routes::<A>::get_foo}: Handler<_, _>` is not satisfied
--> examples/axum/src/main.rs:36:51
|
36 | axum::Router::new().route("/foo", get(Self::get_foo))
| --- ^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extension<A>) -> impl Future<Output = Json<Foo>> {Routes::<A>::get_foo}`
| |
| required by a bound introduced by this call
|
= help: the trait `Handler<T, ReqBody>` is implemented for `Layered<S, T>`
note: required by a bound in `axum::routing::get`
--> /home/dkolsoi/.cargo/registry/src/github.com-1ecc6299db9ec823/axum-0.5.17/src/routing/method_routing.rs:397:1
|
397 | top_level_handler_fn!(get, GET);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `axum::routing::get`
= note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
delegate_by = Borrow
should be deprecated in favor of delegate_by = ref
, which uses AsRef<dyn Trait>
instead of Borrow<dyn Trait>
. The Borrow trait is intended for borrowing the "entire" type.
should reconsider the idea of different "entrait scopes" for mocks.
Alternatives:
the second option could be interesting, because then libraries could more easily support entrait annotations without any cost: i.e. no features would generate no extra code; everything is opt-in. The applications selects the mocking framework it wants via a feature selection. An important thing to consider in that case is that features must have an additive design, features will be the same in every usage of entrait, regardless of which crate invoked it.
An important point to remember about entrait is that (I think) some entrait patterns will be uniform within a single crate, some patterns will be uniform within a whole binary:
feature | "width" |
---|---|
mock library | application/binary wide |
mock cfg(test) | crate wide |
i.e. #[cfg_attr(feature = "?", doc = "some code example")]
Possible solutions:
cargo hack
runs, then only run doctests with specific features turned on. But there seems to be no way to exclude doctests from a test run.In entrait 0.5, I want more focus on dependency inversion and less focus on mocking out every single function.
The main advantages of the entrait pattern for functions are:
Impl<T>
Support for mocking every little function when unimock is enabled is potentially generating a lot more code than necessary.
Instead, by default, we could try to generate a very simple impl<T> Trait for T where T: Dependencies
that could cover both Impl<T>
and mocked environments. Mocking a simple function will become opt-in, with something like #[entrait(Foo, mock)]
).
Mock support should be enabled by default on inverted dependencies - the ones that have no local implementation.
async-trait
to boxed-futures
.async-trait
is just an implementation detail on the way to get boxed futures, and this does not need to be exposed.
## Investigate trait redesign
#11
I am continuing to work with your crate, it's really useful.
One thing where I am stuck: I have an object from an external crate that comes in as an &mut imp SomeTrait
. It is a storage interface. Is there any way I can store this in the application state? I tried, but I get into trouble with Send
and Sync
traits.
This relates to the definition of dependency "leaf nodes".
Entrait apps have to be wrapped in ::implementation::Impl
because of specialization issues. Traits get implemented for Impl<T>
and Unimock
.
Some applications need to be modular/composed of different crates, and linked together at the main crate. Imagine an app consisting of a library and a "final" app in the main crate:
// some lib crate
struct AppModule;
// main crate
struct App {
app_module: Impl<AppModule>
}
In the main crate, we have some functions which want to call into entraited functions from the lib crate. But traits are not implemented for Impl<App>
, but for Impl<AppModule>
. So we need a way to get there, without naming the App
. We only want to mention traits. We need access to all the traits implemented by Impl<AppModule>
while still supporting unimock.
So we can imagine a trait for accessing the app module:
// main crate
trait GetAppModule {
type Target: lib::Trait1 + lib::Trait2 + Send + Sync + 'static;
fn get_app_module(&self) -> &Self::Target;
}
(this cannot be a generic trait because of custom bounds on Target
)
This trait gets implemented for Impl<App>
and Unimock
:
impl GetAppModule for Impl<App> {
type Target = Impl<AppModule>;
fn get_app_module(&self) -> &Self::Target {
&self.app_module
}
}
impl GetAppModule for Unimock {
type Target = Unimock;
fn get_app_module(&self) -> &Self::Target {
self
}
}
This would work fine and we can then write things like:
fn do_with_module(deps: &impl GetAppModule) {
deps.get_app_module().some_lib_function();
}
But it's a lot of boilerplate to generate these impls, and I'd like to make it easier to express this relationship using a macro.
A key point is that we need to repeat all the lib
traits as bounds for GetAppModule
, but it would be better if the lib exported its "public API" traits. It could do that by exporting a "meta trait" that inherits from all its public traits:
// lib
pub trait AppModuleMeta: Trait1, Trait2, Trait2 {}
impl AppModuleMeta for Impl<AppModule> {}
impl AppModuleMeta for Unimock {}
Database transactions require some kind of context value representing a session with the database. The transaction either succeeds or is cancelled in case of an error. In SQLx the transaction object requires a call to .commit()
at the end.
Designing a DB abstraction this way requires an associated type representing the transaction object:
async fn some_db_op(deps: &impl Repository) {
let mut tx = deps.transaction();
deps.insert_something(some_value, &mut tx)?;
tx.commit().await?;
Ok(())
}
(the transaction object is likely different for each implementation of Repository).
I'm not completely sold on the need to explicitly call commit. What about taking advantage of the Result
each db call returns? If it returns some kind of Result<Transaction<T>, Error>
. That Ok context could be used somehow in subsequent calls. At the end of the operation chain, the T
needs to be dug out from the transaction, and that's where the commit happens.
This issue represent exploring the design space around transaction modelling.
TL;DR: The crate that defines an interface should not need to be the crate that implements it. The way entrait is designed, using delegating blanket implementations, requires some more delegation "magic".
The entrait-for-trait feature #[entrait(delegate_by = Borrow)]
only works for leaf dependencies.
Here is an idea for how to do something similar, but without exiting the Impl
layer.
We have some API that we want to potentially implement (downstream) in different dynamic ways:
pub trait Facade {
fn foo(&self) -> i32;
}
We want to implement this trait for Impl<T>
so it can be used as a dependency. But that, by definition, is the only implementation. We want to have a delegation through dynamic dispatch to reach the final destination. Since that trait is already implemented for Impl<T>
, we need another trait! We can use the entrait syntax to generate a trait from a trait ๐ฌ Let's call the new trait Backend
:
#[entrait(pub Backend)]
pub trait Facade {
fn foo(&self) -> i32;
}
The generated trait is generic, and has two receivers:
pub trait Backend<T>: 'static {
fn foo(&self, _impl: &entrait::Impl<T>) -> i32;
}
The generated delegation looks like this:
impl<T: 'static> Facade for Impl<T>
where
T: core::borrow::Borrow<dyn Backend<T>>,
{
fn foo(&self) -> i32 {
self.borrow().foo(self)
}
}
Now the application has to implement Borrow<dyn Backend<Self>>
. This is the manual part.
To define an implementation of Backend<T>
, we can write the following module:
pub struct MyImpl;
#[entrait_impl(some_crate::Backend for MyImpl)] <--- proposed syntax
#[entrait_impl(dyn some_crate::Backend for MyImpl)] <--- dyn version
mod my_impl {
fn foo(deps: &impl Whatever) -> i32 {
42
}
}
The functions inside this module need to match the backend interface. I think the compile errors will be good enough. The implementation is of course auto-generated:
pub struct MyImpl;
impl<T> Backend<T> for MyImpl
where
Impl<T>: Whatever,
{
fn foo(&self, _impl: &Impl<T>) -> i32 {
foo(_impl)
}
}
The Borrow part of the application could either be a Box or some enum. Avoiding allocation could look like this:
impl Borrow<dyn facade::Backend<Self>> for App {
fn borrow(&self) -> &dyn Backend<Self> {
match &self.facade_impl {
AppFacadeImpl::My(my_impl) => my_impl,
AppFacadeImpl::Other(other_impl) => other_impl,
}
}
}
Instead of
#[entrait_impl]
mod some_mod {
#[derive_impl(super::SomeTrait)]
pub struct SomeType;
}
The macro should instead work with:
pub struct SomeType;
#[entrait_impl] // ?
impl SomeTrait for SomeType {
fn foo(deps: &impl SomethingElse) {}
}
which would expand to:
impl SomeType { // <--- removed "SomeTrait for" from this location
fn foo(deps: &impl SomethingElse) {}
}
impl<T> SomeTrait<T> for SomeType { // <--- move into this place
fn foo(__impl: &Impl<T>) {
Self::foo(__impl)
}
}
Hi there, a couple of issues with dependencies I noticed. Thanks for this elegant library, I searched high and low for a solid dependency injection in Rust and this is the only one I think makes sense.
I use cargo build --tests
to build.
Given this cargo.toml
[package]
name = "entrait-issue"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
entrait = "0.5.3"
and this main.rs
use entrait::entrait;
#[entrait(Foo, unimock, mock_api=FooMock)]
fn foo<D>(_: &D) -> i32 {
unimplemented!()
}
#[entrait(MyMod, unimock, mock_api=mock)]
mod my_mod {
pub fn bar<D>(_: &D) -> i32 {
unimplemented!()
}
}
fn main(){}
I get the error error[E0433]: failed to resolve: could not find `__unimock` in `entrait
When I switch to the unimock feature in cargo.toml
things get better.
[package]
name = "entrait-issue"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
entrait = { version = "0.5.3", features = ["unimock"] }
[dev-dependencies]
unimock = "0.5.3"
Here is the main.rs
for this case
use entrait::entrait;
#[entrait(Foo, mock_api=FooMock)]
fn foo<D>(_: &D) -> i32 {
unimplemented!()
}
#[entrait(MyMod, mock_api=mock)]
mod my_mod {
pub fn bar<D>(_: &D) -> i32 {
unimplemented!()
}
}
fn my_func(deps: &(impl Foo + MyMod)) -> i32 {
deps.foo() + deps.bar()
}
fn main() {}
#[cfg(test)]
mod tests {
use super::*;
use unimock::*;
#[test]
fn test() {
let mocked_deps = Unimock::new((
FooMock.each_call(matching!()).returns(40),
my_mod::mock::bar.each_call(matching!()).returns(2),
));
assert_eq!(42, my_func(&mocked_deps));
}
}
Still, the compiler complains no method named `each_call` found for struct `FooMock` in the current scope
What I did to work around this problem was to import entrait::__unimock::*
instead of use unimock::*;
.
Am I overlooking something (quite likely) or are these bugs?
Hi there,
Thanks for the library as well as the concept! I might have discovered a case of incorrect expansion in a certain scenario. I'll try to explain below:
I have a handwritten trait named Foo
in crate A which I then want to implement as MyFoo
in crate B.
The expansion in crate B is causing errors due to the lifetime specified in the method I prescribed in my handwritten trait.
The DoFoo
method in my trait contains a lifetime parameter (my real implementation involves tokenizing some input string, and preferably I would like to avoid clones).
#[entrait(FooImpl, delegate_by = FooDelegate)]
trait Foo {
fn DoFoo<'a>(&self, input: &'a str) -> &'a str;
}
trait Foo {
fn DoFoo<'a>(&self, input: &'a str) -> &'a str;
}
trait FooImpl<EntraitT>: 'static {
fn DoFoo<'a>(__impl: &::entrait::Impl<EntraitT>, input: &'a str) -> &'a str;
}
pub trait FooDelegate<T> {
type Target: FooImpl<T>;
}
impl<EntraitT: Sync> Foo for ::entrait::Impl<EntraitT>
where
EntraitT: FooDelegate<EntraitT> + Sync + 'static,
{
#[inline]
fn DoFoo<'a>(&self, input: &'a str) -> &'a str {
<EntraitT::Target as FooImpl<EntraitT>>::DoFoo(self, input)
}
}
struct MyFoo;
#[entrait]
impl FooImpl for MyFoo {
fn DoFoo<'a, D>(deps: &D, input: &'a str) -> &'a str {
input
}
}
impl MyFoo {
fn DoFoo<'a, D>(deps: &D, input: &'a str) -> &'a str {
input
}
}
// Issue lies here: as the lifetime is added to the impl for the trait and in the wrong order (it is not needed at all)
impl<EntraitT: Sync, 'a> FooImpl<EntraitT, 'a> for MyFoo {
#[inline]
fn DoFoo<'a>(__impl: &::entrait::Impl<EntraitT>, input: &'a str) -> &'a str {
Self::DoFoo(__impl, input)
}
}
I am using version 0.5.3
of the crate.
Suppose I have something like:
#[cfg(feature = "baz")]
struct Baz;
#[entrait]
trait Foo {
fn bar();
#[cfg(feature = "baz")]
fn baz() -> Baz;
}
You'll get an error saying Baz doesn't exist when you don't provide the baz feature flag - entrait doesn't seem to propagate cfgs to the output code.
Both functions give me compiler errrors for cargo build --tests
. I use entrait with the unimock
feature as dependency. Is there a way to make them work with entrait
/unimock
?
use entrait::entrait;
pub trait MyTrait {}
#[entrait(Foo, mock_api=FooMock)]
fn foo<D>(_: &D, _foo: &impl MyTrait) -> i32 {
unimplemented!()
}
#[entrait(Bar, mock_api=BarMock)]
fn bar<D>(_: &D, _bar: &mut impl MyTrait) -> i32 {
unimplemented!()
}
fn main() {}
Errors:
error[E0658]: `impl Trait` in type aliases is unstable
--> src/main.rs:6:25
|
6 | fn foo<D>(_: &D, _foo: &impl MyTrait) -> i32 {
| ^^^^^^^^^^^^
|
= note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information
error[E0658]: `impl Trait` in type aliases is unstable
--> src/main.rs:11:29
|
11 | fn bar<D>(_: &D, _bar: &mut impl MyTrait) -> i32 {
| ^^^^^^^^^^^^
|
= note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information
error: unconstrained opaque type
--> src/main.rs:6:25
|
6 | fn foo<D>(_: &D, _foo: &impl MyTrait) -> i32 {
| ^^^^^^^^^^^^
|
= note: `Inputs` must be used in combination with a concrete type within the same impl
error[E0599]: the method `unimock_try_debug` exists for mutable reference `&mut _::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}`, but its trait bounds were not satisfied
--> src/main.rs:10:1
|
10 | #[entrait(Bar, mock_api=BarMock)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`&mut _::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}: Debug`
which is required by `&mut _::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}: ProperDebug`
`_::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}: Debug`
which is required by `_::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}: ProperDebug`
= note: this error originates in the attribute macro `::entrait::__unimock::unimock` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> src/main.rs:6:18
|
5 | #[entrait(Foo, mock_api=FooMock)]
| --------------------------------- arguments to this function are incorrect
6 | fn foo<D>(_: &D, _foo: &impl MyTrait) -> i32 {
| ^^^^ ------------
| | |
| | the expected opaque type
| | this type parameter
| expected `&_::<impl MockFn for ...>::Inputs<'_>::{opaque#0}`, found `&impl MyTrait`
|
= note: expected reference `&_::<impl MockFn for FooMock>::Inputs<'_>::{opaque#0}`
found reference `&impl MyTrait`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
note: function defined here
--> /Users/ander/.cargo/registry/src/github.com-1ecc6299db9ec823/unimock-0.4.12/src/macro_api.rs:209:8
|
209 | pub fn eval<'u, 'i, F>(unimock: &'u Unimock, inputs: F::Inputs<'i>) -> Evaluation<'u, 'i, F>
| ^^^^
error[E0308]: mismatched types
--> src/main.rs:11:18
|
10 | #[entrait(Bar, mock_api=BarMock)]
| --------------------------------- arguments to this function are incorrect
11 | fn bar<D>(_: &D, _bar: &mut impl MyTrait) -> i32 {
| ^^^^ ------------
| | |
| | the expected opaque type
| | this type parameter
| expected `&mut _::<impl MockFn for ...>::Inputs<'_>::{opaque#0}`, found `&mut impl MyTrait`
|
= note: expected mutable reference `&mut _::<impl MockFn for BarMock>::Inputs<'_>::{opaque#0}`
found mutable reference `&mut impl MyTrait`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
note: function defined here
--> /Users/ander/.cargo/registry/src/github.com-1ecc6299db9ec823/unimock-0.4.12/src/macro_api.rs:209:8
|
209 | pub fn eval<'u, 'i, F>(unimock: &'u Unimock, inputs: F::Inputs<'i>) -> Evaluation<'u, 'i, F>
| ^^^^
Some errors have detailed explanations: E0308, E0599, E0658.
For more information about an error, try `rustc --explain E0308`.
As the author, it's a little unclear to me whether the documentation of this crate is adequate and understandable. I fear that my imagination on how the crate can be used is somewhat limited, because I have to think about the technical details at the same time. I'd appreciate external contributions from a pure usability-perspective, high-level improvement tips, describing novel design patterns, etc.
I don't know whether this will work, but consider:
impl<T> Trait for Impl<T> {
fn foo(&self) {}
}
vs.
impl<T> Trait for &Impl<T> {
fn foo(self) {}
}
If the traits always take self
by value, it might not be necessary to duplicate the traits when doing dependency inversion, because then we can make a newtype without reborrowing:
struct NewType<'a, T>(&'a Impl<T>);
impl<'a, T> Trait for NewType<'a, T> {
fn foo(self) { self.0.foo() }
}
The main reason it was necessary to duplicate the trait with the current design, was that it was impossible to make futures implement Send
when "reborrowing" for a struct like NewType
. NewType
had to be passed as a reference into the delegation-target, referencing the value just created on the stack, and things referencing a value owned by the stack (vs. the heap) can't be sent to another thread. If NewType
is passed by-value, this restriction is likely lifted.
Hey @audunhalland, another associated type issue here!
#[entrait::entrait]
pub trait Client {
type DatabaseImpl;
fn database(&self, name: &str) -> Self::DatabaseImpl;
}
The associated type disappears from the generated trait:
pub trait MongoClient {
fn database(&self, name: &str) -> Self::DatabaseImpl;
}
It also needs to be bound in the entrait::Impl
block as:
type DatabaseImpl = <EntraitT as MongoClient>::DatabaseImpl;
Currently, the naming of generated unimock types is very inconsistent:
entrait | unimock top-level item | MockFn path |
---|---|---|
#[entrait(Foo)] fn foo() {} |
new module foo |
foo::Fn |
#[entrait(Foo)] mod foo { fn bar() {} } |
flattened inside foo |
foo::bar::Fn |
#[entrait] trait Foo { fn bar() } |
top-level struct | Foo__bar |
I wish to make these more consistent. The way Mockall
does it seems better: always make some Mock*
struct that represents the mockable trait.
This is the suggestion:
entrait | unimock top-level item | MockFn path |
notes |
---|---|---|---|
#[entrait(Foo)] fn foo() {} |
MockFoo |
MockFoo() |
the MockFn struct is defined as an empty tuple struct |
#[entrait(Foo)] mod foo { fn bar() {} } |
MockFoo |
MockFoo::bar() |
|
#[entrait] trait Foo { fn bar(); } |
MockFoo |
MockFoo::bar() |
This should be part of the next major version of unimock (0.4), where the defaults will be changed from Foo__bar
to Foo::bar()
Hi,
I wonder if there's a way to have multiple implementations of the same trait?
Something like:
fn login(deps: &impl UserRepository, username: &str) {
deps.user_exists(&username)
}
fn postgres_user_exist(pool: &PgPool, username: &str) {
// do pg stuff
}
fn inmemory_user_exist(data: &Map, username: &str) {
// check in memory
}
And in the main.rs I would like to switch between postgres and in-memory based on, say, a config variable.
Thanks! ๐
delegate_by=Borrow
#[entrait(dyn)}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.