murarth / gumdrop Goto Github PK
View Code? Open in Web Editor NEWRust option parser with custom derive support
License: Apache License 2.0
Rust option parser with custom derive support
License: Apache License 2.0
I recently switched a project from Structopt to Gumdrop and am generally quite happy with the change. However, one thing I've not been able to figure out how to do is to use a const
to define a default
value.
Essentially, I define a default port and use this in several places in the code:
/// Constant defining Goose's default port when running a Gaggle.
const DEFAULT_PORT: &str = "5115";
One of the places I was able to use it with structopt
was in defining the default value for a couple of run-time configuration options. Is there any way to do this with gumdrop?
For example, if I try this:
/// Sets port Manager listens on
#[options(no_short, default = DEFAULT_PORT, meta = "PORT")]
pub manager_bind_port: u16,
I get the following error:
error: expected literal
--> src/lib.rs:1945:35
|
1945 | #[options(no_short, default = DEFAULT_PORT, meta = "PORT")]
| ^^^^^^^^^^^^
Currently, it doesn't mentioned anywhere.
And it will be nice to have a list of all possible options/attributes.
A filename or path is not a String
, but OsString
. I don't see any mention of OS strings in crate-level documentation for gumdrop.
Also I see parse(try_from_str
and parse(from_str
, but I don't see the exhausive list of what I can put into parse(...)
. Is there from_os_str
?
Simiarly to how it's done in failure.
I think it makes using library a bit easier without any downsides.
As far as I can tell, if you put a help
option in your options struct, gumdrop
magically notices it and adds behavior to print out a help messages for you. Also as far as I can tell, nowhere in the docs or examples does it actually talk about this.
#[derive(Debug, gumdrop::Options)]
struct Args {
/// Which base58 alphabet to decode/encode with [possible values: bitcoin, monero,
/// ripple, flickr or custom(abc...xyz)]
alphabet: Alphabet,
help: bool
}
This will only display the first line of doc comment in --help
.
#[derive(Debug, gumdrop::Options)]
struct Args {
/** Which base58 alphabet to decode/encode with [possible values: bitcoin, monero,
ripple, flickr or custom(abc...xyz)] */
alphabet: Alphabet,
help: bool
}
This retains the indentation from the multi-line doc comment resulting in misaligned text in --help
Optional arguments:
-a, --alphabet ALPHABET Which base58 alphabet to decode/encode with [possible values: bitcoin, monero,
ripple, flickr or custom(abc...xyz)]
Commonly programs allow specifying --help
with no other arguments, even if those other arguments would be "required" otherwise. This isn't true in gumdrop today, or at least it isn't working for me.
I've spend about 10 minutes working on a project using Gumdrop, reading the docs, examples etc, but I can't find anywhere saying how to have required arguments, something like #[required]
.
Hi,
There should be a way to specify various argument grouping and constraints, e.g. "-f implies -a and -b" or "-n, -m, -l are mutually exclusive".
The crate versions specified in gumdrop_drive's Cargo.toml file are too low. It can't compile when using those versions. To demonstrate, do
$ cargo update -Zminimal-versions
$ cargo check
on any project that uses gumpdrop. It will fail with errors like this:
error[E0599]: no method named `get_ident` found for reference `&syn::Path` in the current scope
--> /home/somers/.cargo/registry/src/github.com-1ecc6299db9ec823/gumdrop_derive-0.8.0/src/lib.rs:1039:52
|
1039 | Meta::Path(path) => match path.get_ident() {
| ^^^^^^^^^ help: there is an associated function with a similar name: `is_ident`
It would be nice if gumdrop would support -v
-vv
-vvv
-vvvv
as input for setting the verbosity level
Like:
fn my_func(value: &str) -> Option<u32> {} // or Result
#[options(help = "Sets the target DPI", meta = "DPI", default = "96", validate=my_func)]
dpi: Option<u32>,
I think no_short
should be the default. Not only because short options are a scarce resource, but also for forwards compatibility:
I know it's not backward compatible. But I always feel on minefield when adding an option with autogenerated shortcut. What do you think?
Keeping the source formatted automatically allows anyone that wants to contribute to just format the source automatically for their changes, without introducing a large delta in existing code.
For any preference in formatting style that differs from the default, rustfmt
supports configuration via rustfmt.toml.
While a formatting PR is easy, to keep code formatted in the future, a CI item can be added.
Currently, if I add a default = "8000"
to my port: Option<u16>
without removing the Option
, it gives the following error message.
the trait `std::str::FromStr` is not implemented for `std::option::Option<u16>`
I found this extremely confusing because I didn't try compiling it before adding the default
and the documentation doesn't mention that adding default
will narrow the set of valid type signatures, and the Option<i32>
example from the docs was also breaking.
(I went in circles double-checking these examples for 15 minutes and looking at the FromStr
API docs before it occurred to me that maybe default
altered the set of valid types.)
The documentation for default
should be amended to mention this.
docs.rs page list possible variants for #[options(...)]
, but I don't see name
there.
It can be used to rename subcommands.
This would potentially be a breaking API change, but I'd like to suggest that the Option
type be used for optional arguments.
Currently, Option
seems to be superfluous, as arguments are still optional unless the "required" meta flag is set.
In addition, the following scenario seems to be unachievable with the current implementation:
As an example, let's say a command-line tool normally has output that is unsorted. If you pass the -s
argument in, then output should become sorted in some default way (e.g. alphabetically). Additionally, if you pass a value to the -s
argument, you can specify the way in which the sort occurs (e.g. "-sa" for alphabetical, "-sn" for numerical).
In my case, the "principal of least surprise" led me to think that the following might achieve that result:
#[derive(Debug, Options)]
struct GramsOptions {
#[options(help = "sort tallied output: a=alphabetic [DEFAULT], n=numeric")]
sort: Option<String>,
}
I expected I might be able to parse this as follows:
let sort: Option<Sort> = match opts.sort.as_ref().map(|s| &s[..]) {
Some("a") => Some(Sort::Alphabetic),
Some("n") => Some(Sort::Numeric),
None => Some(Sort::Alphabetic),
};
Currently EarlyExit
output is written to stdout, but stderr is more appropriate for diagnostics and any user-directed messages that should never end up in a pipeline.
I noticed the subcommands example talks about using a dedicated help
subcommand, and it helps that the command_usage
API is tailored for it. However I also want to support --help
for every subcommand to do the same thing which unfortunately requires lots of verbose code:
extern crate gumdrop;
#[macro_use] extern crate gumdrop_derive;
use std::io::Write;
use gumdrop::Options;
#[derive(Debug, Default, Options)]
struct MainOptions {
#[options(help = "Print the help message")]
help: bool,
#[options(command)]
subcommand: Option<SubCommand>,
}
#[derive(Debug, Options)]
enum SubCommand {
#[options(help = "Does foo")]
Foo(FooOptions),
#[options(help = "Does bar")]
Bar(BarOptions),
}
#[derive(Debug, Default, Options)]
struct FooOptions {
#[options(help = "Print the help message")]
help: bool,
#[options(help = "The thing to foo")]
thing: String,
}
#[derive(Debug, Default, Options)]
struct BarOptions {
#[options(help = "Print the help message")]
help: bool,
#[options(help = "The thing to bar")]
thing: String,
}
fn main() {
let args: Vec<_> = std::env::args().collect();
let options: MainOptions = gumdrop::parse_args_default(&args[1..]).unwrap_or_else(|err| {
// Invalid args
writeln!(std::io::stderr(), "{}", err);
print_usage(std::io::stderr());
std::process::exit(1);
});
if options.help {
// Explicitly asked for help
print_usage(std::io::stdout());
return;
}
let subcommand = options.subcommand.unwrap_or_else(|| {
// Did not provide subcommand (required)
print_usage(std::io::stderr());
std::process::exit(1);
});
let subcommand_usage = match subcommand {
SubCommand::Foo(FooOptions { help: true, .. }) => Some(("foo", FooOptions::usage())),
SubCommand::Bar(BarOptions { help: true, .. }) => Some(("bar", BarOptions::usage())),
_ => None,
};
if let Some((subcommand_name, subcommand_usage)) = subcommand_usage {
// Explicitly asked for subcommand help
print_subcommand_usage(std::io::stdout(), subcommand_name, subcommand_usage);
return;
}
// Run the subcommand
match subcommand {
SubCommand::Foo(FooOptions { thing, .. }) => println!("Foo the thing {}", thing),
SubCommand::Bar(BarOptions { thing, .. }) => {
if thing.is_empty() {
writeln!(std::io::stderr(), "Invalid thing");
print_subcommand_usage(std::io::stderr(), "bar", BarOptions::usage());
std::process::exit(1);
}
println!("Bar the thing {}", thing)
},
}
}
fn print_usage<W: Write>(mut w: W) {
writeln!(w, "{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
writeln!(w, "{}", env!("CARGO_PKG_AUTHORS"));
writeln!(w, "{}", env!("CARGO_PKG_DESCRIPTION"));
writeln!(w);
writeln!(w, "USAGE:");
writeln!(w, " {} <SUBCOMMAND>", env!("CARGO_PKG_NAME"));
writeln!(w);
writeln!(w, "FLAGS");
writeln!(w, "{}", MainOptions::usage());
writeln!(w);
writeln!(w, "SUBCOMMANDS:");
writeln!(w, "{}", SubCommand::usage());
}
fn print_subcommand_usage<W: Write>(mut w: W, subcommand_name: &'static str, subcommand_usage: &'static str) {
writeln!(w, "USAGE:");
writeln!(w, " {} {} [ARGUMENTS]", env!("CARGO_PKG_NAME"), subcommand_name);
writeln!(w);
writeln!(w, "FLAGS:");
writeln!(w, "{}", subcommand_usage);
}
The expectations are that:
cargo run --
should print the top-level usage text, and exit with 1.
cargo run -- --help
should print the top-level usage text, and exit with 0.
cargo run -- foo --help
should print the usage text for the foo
subcommand, and exit with 0.
cargo run -- bar
should get an Invalid thing
error, print the usage text for the bar
subcommand, and exit with 1.
cargo run -- baz
should get an unrecognized command
error, print the top-level usage text, and exit with 1.
There are two bad things about this code:
I have to see if the user asked for subcommand help by matching over every subcommand variant and extracting its help
field.
For printing the usage of a matched subcommand, I want to print the name of the matched subcommand, but I can't get it. Instead I have to repeat it myself (the "foo"
in Some(("foo", FooOptions::usage()))
). gumdrop already knows the subcommand name since it matched on it in SubCommand::parse_command
, but it doesn't give a way to get it from the parsed value.
It would be nice if:
the struct member holding the subcommand enum (MainOptions::subcommand
in the above example) could also know the name of the subcommand it has matched. Perhaps gumdrop could see if it's Option<SubCommand>
or Option<(SubCommand, &'static str)>
, and additionally put the name in the latter case.
there was some built-in handling for detecting the presence of --help
and determining whose usage text should be printed (top-level or a subcommand). I'm not asking for something that actually prints the usage, ie, I'm not asking for a replacement for the print_usage
and print_subcommand_usage
functions. Rather I'm asking for a replacement for the match
block that checks for help: true
in every subcommand.
So all together, it could be used like this:
#[derive(Debug, Default, Options)]
struct MainOptions {
#[options(help = "Print the help message")]
help: bool,
#[options(command)]
subcommand: Option<(SubCommand, &'static str)>, // Tuple here
}
and
if options.help_requested() { // New function on gumdrop::Options
// Explicitly asked for help
print_usage(std::io::stdout());
return;
}
let (subcommand, subcommand_name) = options.subcommand.unwrap_or_else(|| {
// Did not provide subcommand (required)
print_usage(std::io::stderr());
std::process::exit(1);
});
if subcommand.help_requested() { // New function on gumdrop::Options
// Explicitly asked for subcommand help
print_subcommand_usage(std::io::stdout(), subcommand_name, subcommand.command_usage(subcommand_name).unwrap());
return;
}
where the first fn help_requested(&self) -> bool
function checks for self.help == true
and the second fn help_requested(&self) -> bool
function checks for help == true
in any of the variants of the subcommand.
With the source below:
use gumdrop::Options;
use std::{env, fs, process};
#[derive(Debug, Options)]
struct Args {
#[options(help = "Prints the version", short = "v", long = "version")]
version: bool,
#[options(help = "Calls the list_imports function with a filename", short = "l", long = "list-imports")]
list_imports: Option<String>,
#[options(help = "Updates DLL bindings for <from> so it points to <to>", short = "s", long = "set-import")]
set_import: bool,
#[options(free)]
free_args: Vec<String>,
#[options(help = "Print this help message and exit", short = "h")]
help: bool,
}
fn main() {
let args = Args::parse_args_default_or_exit();
if args.help {
println!("{}", Args::usage());
return;
}
if args.version {
println!("fixPath version 1.0"); // FIXME read from cargo.toml
} else if let Some(filename) = args.list_imports {
println!("{}", filename);
} else if args.set_import {
if args.free_args.len() == 3 {
println!(
"set-import: {}, {}, {}",
args.free_args[0], args.free_args[1], args.free_args[2]
);
} else {
eprintln!("Error: --set-import requires exactly 3 arguments.");
}
} else {
eprintln!("Error: one of --version, --list-imports or --set-import must be provided.");
println!("{}", Args::usage());
process::exit(1);
}
}
why does the text LIST-IMPORTS in call caps appear?!
.\fixPath.exe -h
Usage: C:\Users\joschie\Desktop\Projects\fixPath\target\debug\fixPath.exe [OPTIONS]
Positional arguments:
free_args
Optional arguments:
-v, --version Prints the version
-l, --list-imports LIST-IMPORTS
Prints <file>'s DLL bindings
-s, --set-import Updates <file>'s DLL bindings <from> so it points to <to>
-h, --help Print this help message and exit
I'm not familiar with procedural macros, but is it possible to depend only on proc-macro
? The problem is that proc-macro2
and syn
a very slow to build:
> cargo bloat --release --time -j1
Compiling ...
Time Crate
44.36s syn
21.22s gumdrop_derive
5.42s proc_macro2
1.55s quote
0.48s gumdrop
0.12s unicode_xid
On the other hand, arguments parsing without macros is too verbose.
Add support for a default value.
#[options(help = "Sets the target DPI", meta = "DPI", default = "96")]
dpi: u32,
If it possible it would be great to set a type instead of a string as a default value.
Is it possible to use this crate and show the usage information automatically if arguments are missing or parsing fails? This is the default behavior in structopt
and clap
.
Unsure what my issue is here, but it seems that environment variables are not being parsed for me.
It would also be nice to have an optional argument to pass to parse_args_default_or_exit
and similar to disable the printing/parsing of environment variables.
There's a note saying it's doesn't show in usage in the code.. but why not?
Crate-level documentation is all about
#[options(help = "help text")]
item: Type,
, but I don't see occurence of
/// help text
#[options]
item: Type
Is it supposered? If yes, some of documentation examples should use it.
Would it be possible to add an option to gumdrop_derive that skips one of the struct's fields? Or if not, one that hides it from the help menu?
My motivation is to combine gumdrop with confy on a single struct. Most options should be configurable either from the command line or from the config file, but a few options are only relevant for one or the other. Here's an example of how to use such an option:
#[derive(Debug, Default, Deserialize, Options, Serialize)]
struct Config {
#[options(help = "print help message")]
// it makes no sense to set --help in the config file
#[serde(skip)]
help: bool,
/// Thread pool size
// This one makes sense both both config file and CLI
#[options(default = "1")]
threads: i32,
/// Specify the server's parameters in JSON
// This field is too complicated for the command-line. Only enable it in the config file
#[options(skip)]
serverspec: Option<String>
}
If you're already assigning magical behaviour to naming a boolean argument help
, it seems like a wart to not include a default help
string in that magic behaviour.
Among other reasons, if the help string is provided by default, most users will only need the single help: bool,
line for it.
I'm trying to use a function called default_config()
to provide the default value for an option:
pub fn default_config() -> String {
option_env!("BIN_DEFAULT_CONFIG_PATH")
.unwrap_or("/etc/default/path")
.to_owned()
}
#[derive(Options)]
pub struct FetchOpts {
#[options(help = "Path to the config file", default = "default_config()")]
pub config: String,
}
However, that is interpreted as the literal value "default_config()"
. Passing default = default_config()
causes a syntax error panic instead.
Is there a sensible way to set that default value, taking advantage of the help text auto-generation? (If not, I can fall back to the unwrap method, which I was previously using)
It would be really nice if the following worked, in order to enable re-use of options between multiple commands:
#[derive(Options)]
struct CommonOpts {
foo: bool,
}
#[derive(Options)]
struct Opts {
bar: bool,
#[options(flatten)]
common: CommonOpts,
}
// call using `program --foo --bar`
Given the following:
fn parse_username(input: &str) -> Result<String, String> {
// always fails
Err("Username is invalid!")
}
#[derive(Options)]
pub struct CliOptions {
#[options(free, parse(try_from_str = "parse_username"))]
pub username: String,
}
As of commit 121667e
, I get the following error message when passing a username (which is designed to fail, here):
[bin_name]: invalid argument to option `free`: Username is invalid!
I attempted to describe it using meta to change the identifier free
, but meta and free are mutually exclusive. Is there a way to change free
to username
, in this scenario?
I don't like the style of this crate's error messages ("unexpected free argument" vs "too many arguments") so I wanted to rewrite the error messages myself by catching errors and matching on them. However, once I obtain an Error struct, I can't actually extract the kind out of it because it's private and there's no getter!
:(
Is there a possibility to support something like this:
#[derive(Debug, Default, Options)]
struct CreateOptions {
#[options(no_short, help = "some parameter")]
test_parameter: Vec<(String,String)>,
}
so you can run commands like this:
./mybinary --test-parameter VARIABLE VALUE --test-parameter ANOTHERVARIABLE ANOTHERVALUE
That would be nice to have
Right now, I have the following struct:
#[derive(Options)]
struct Arguments {
/// print help message
help: bool,
#[options(free,required)]
/// The file to extract data from
source: PathBuf,
#[options(free,required)]
/// The file to insert the data into
target: PathBuf,
}
When I call the program with '--help', I get the following usage:
Usage: clitool [OPTIONS]
Positional arguments:
source The file to extract data from
target The file to insert the data into
Optional arguments:
-h, --help print help message
In almost every other command line tool on my system, that first line of Usage would be something like: Usage: clitool [OPTIONS] SOURCE TARGET
.
Right now I have to duplicate the code from parse_args_or_exit
in order to add this.
It would be nice if gumdrop did this automatically, or at least allowed me to customize the usage line.
Now everything is optional, is there a possibility to make a flag required ?
for example
./myapp --server myapp.com
and when --server
flag is not passed the app throws an error.
or is this already possible and I'm just looking over it ?
I know I can go manually over all the flags and throw an error, but I wonder if there is an automatic way
Using gumpdrop 0.7.0
with #[derive(Options)]
does not support nested subcommands in the built-in help output.
For an example program, here's myprogram
which allows users to manage user accounts and organizations with a command structure similar to this:
$ mycommand account new
$ mycommand account update --name alice
$ mycommand org add-member
etc...
The problem is that the behavior of --help
seems unaware of subcommands. Here's an example main demonstrating the behavior for the above command:
use gumdrop::Options;
// Define options for the program.
#[derive(Debug, Options)]
pub struct MainOptions {
#[options(help = "print help message")]
help: bool,
#[options(command)]
command: Option<Command>,
}
#[derive(Debug, Options)]
pub enum Command {
#[options(help = "manage an account")]
Account(AccountOptions),
#[options(help = "manage an organization")]
Org(OrgOptions),
}
#[derive(Debug, Options)]
pub struct AccountOptions {
#[options(help = "print help message")]
help: bool,
#[options(command)]
command: Option<AccountCommand>,
}
#[derive(Debug, Options)]
pub enum AccountCommand {
#[options(help = "create a new account")]
New(AccountNewOptions),
#[options(help = "update account details")]
Update(AccountUpdateOptions),
}
#[derive(Debug, Options)]
pub struct AccountNewOptions {
#[options(help = "print help message")]
help: bool,
}
#[derive(Debug, Options)]
pub struct AccountUpdateOptions {
#[options(help = "print help message")]
help: bool,
#[options(help = "set account name")]
name: String,
}
// This is just empty scaffolding for now:
#[derive(Debug, Options)]
pub struct OrgOptions {}
fn main() {
let opts = MainOptions::parse_args_default_or_exit();
println!("{:#?}", opts);
}
The following snippets show the behavior of --help
along with desired changes.
$ mycommand --help
Usage: mycommand [OPTIONS]
Optional arguments:
-h, --help print help message
Available commands:
account manage an account
org manage an organization
This output works well and supports the top-level subcommands well.
$ mycommand --help account
Usage: mycommand account [OPTIONS]
Optional arguments:
-h, --help print help message
This output shows the right commandline to include the account
command, but it doesn't show the account's subcommands new
and update
.
$ mycommand account --help
Usage: mycommand account [OPTIONS]
Optional arguments:
-h, --help print help message
This behavior is identical to --help account
which is my desired behavior, based on a common pattern with cli tools. Fixing the bug in the --help account
approach should maintain this synonymous behavior for this case.
$ mycommand account --help new
Usage: mycommand account [OPTIONS]
Optional arguments:
-h, --help print help message
This shows the same output as without the new
. Instead I request that it shows the full command as mycommand account new [option]
plus the new
specific options.
$ mycommand account new --help
Usage: mycommand account [OPTIONS]
Optional arguments:
-h, --help print help message
Just as with the account --help
and --help account
cases, this case should be synonymous with the previous case to account --help new
.
Finally, this request should apply to an arbitrary depth of subcommands. Is this request coherent and possible? Are there any problems with this design or approach?
Structure is:
#[derive(Options, Debug, Default)]
#[options(no_short)]
struct MyOptions {
#[options(help="Print help message and exit")]
help: bool,
#[options(free, help="filename")]
filenames: Vec<String>,
}
Error is:
= help: message: `free` and `help` are mutually exclusive
What's wrong with describing positional arguments? Use case is kinda like in ls
command. Maybe there is other way to implement positional arguments instead of free
? (which is a weird name for the task indeed).
With a struct like
#[derive(Debug, Options)]
pub(crate) struct RawConfig {
#[options(free, required, help = "File to run")]
pub file_to_run: String,
....
}
When run without any free parameters I get:
missing required free argument
Where I'd hope to get something like missing required argument file_to_run
or such. I think users of this application will not understand what the free argument means, probably will try to call with a --free
option or something.
It would be nice to have a single function which does the following:
I think this is important because even if front page examples there there are few mistakes: (1) no error code returned, (2) error message is printed to stdout not stderr.
What do you think? Is there any limitations of help that could be generated by such function? I.e. a problem with subcommands or something?
fn parse_languages(s: &str) -> Result<Vec<String>, &'static str> {
let mut langs = Vec::new();
for lang in s.split(',') {
langs.push(lang.trim().to_string());
}
Ok(langs)
}
// ...
#[options(no_short, meta = "LANG", default = "en", parse(try_from_str = "parse_languages"))]
languages: Vec<String>,
error[E0308]: try expression alternatives have incompatible types
--> src/main.rs:81:17
|
81 | #[derive(Debug, Options)]
| ^^^^^^^
| |
| expected struct `std::string::String`, found struct `std::vec::Vec`
| help: try wrapping with a success variant: `Ok(Options)`
|
= note: expected type `std::string::String`
found type `std::vec::Vec<std::string::String>`
In the docs it says parse_args_default_or_exit
"parses arguments from the environment, using the default parsing style." but it's not clear what that means?
Currently, the gumdrop_derive docs on docs.rs look like this:
...which makes it needlessly difficult to read them. The simplest solution is to add some bullet points.
Hi.
Is there any option to remove the command line from the error messages? For example, supposing we passa an invalid argument, it is reported like this:
$ ./some/portable/media/test -d
./some/portable/media/test: unrecognized option -d
So, if you are using a screen reader like Orca or NVDA, you will hear "dot" "bar" "some" "bar" "portable" "bar" "media" "bar" "test" and finally hear the error message "unrecognized option -d".
cheers
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.