use clap::App;
mod analyze {
use super::message;
use super::diagnostics;
use anyhow::{anyhow, Result};
use clap::{App, Arg, ArgMatches};
use idl::builder::open_directory;
use message::Message;
use std::path::Path;
use std::path::PathBuf;
pub fn create_command<'a>() -> App<'a> {
App::new("analyze")
.about("Analyze the project")
.args(&[Arg::new("input")
.help("Package path")
.short('i')
.long("input")
.takes_value(true)])
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
let input: PathBuf;
let working_dir = std::env::current_dir()?;
input = match matches.value_of("input") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir.clone(),
};
let mut module = open_directory(&input)?;
module.update()?;
if !diagnostics::diagnostic(&module)? {
Message::info("Ok".to_owned())?;
Ok(())
} else {
Err(anyhow!(""))
}
}
}
mod clean {
use anyhow::Result;
use clap::{App, Arg, ArgMatches};
use idl::builder::open_directory;
use std::fs;
use std::path::Path;
pub fn create_command<'a>() -> App<'a> {
App::new("clean").about("Clean generated files").args(&[
Arg::new("output")
.help("Output path")
.short('o')
.default_value(".")
.long("output")
.takes_value(true),
Arg::new("input")
.help("Package path")
.default_value(".")
.short('i')
.long("input")
.takes_value(true),
])
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
let input = matches.value_of("input").unwrap();
let output = matches.value_of("output").unwrap();
let mut module = open_directory(std::path::Path::new(input))?;
module.update_ids_analyzer()?;
let package_name = module.package_name()?;
let _ = fs::remove_dir_all(Path::new(output).join(&package_name));
Ok(())
}
}
mod client {
use super::diagnostics;
use super::message;
use anyhow::{anyhow, Result};
use clap::{App, Arg, ArgMatches};
use convert_case::{Case, Casing};
use idl::builder::{binary_json, get_all_idl_nodes, open_directory};
use idl::language::*;
use message::Message;
use std::path::Path;
use std::{fs, path::PathBuf};
pub fn create_command<'a>() -> App<'a> {
App::new("client").about("Generate client files").args(&[
Arg::new("output")
.help("Output path")
.short('o')
.long("output")
.conflicts_with("path")
.takes_value(true),
Arg::new("input")
.help("Idl input path")
.short('i')
.long("input")
.conflicts_with("path")
.takes_value(true),
Arg::new("path")
.help("The target directory")
.long("path")
.takes_value(true)
.conflicts_with_all(&["input", "output"]),
Arg::new("server")
.help("Server")
.long("server")
.takes_value(true),
Arg::new("client")
.help("Client")
.long("client")
.default_value("Main")
.takes_value(true),
Arg::new("debug")
.help("Debug mode")
.long("debug")
.takes_value(false)
.conflicts_with_all(&["clean, no_build"]),
Arg::new("no_build")
.help("Skip building the server files")
.long("no-build")
.takes_value(false)
.conflicts_with_all(&["clean", "debug"]),
Arg::new("only_build")
.help("Only builds the server without any client code generation")
.long("only-build")
.takes_value(false)
.conflicts_with_all(&["clean", "no_build"]),
Arg::new("clean")
.help("Remove all generated files")
.long("clean")
.conflicts_with_all(&["output", "print"])
.takes_value(false)
.conflicts_with("no_build"),
Arg::new("print")
.help("Prints the request in a JSON format")
.long("print")
.takes_value(true)
.possible_values(&["client", "server", "client-response", "server-response"]),
])
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
let input: PathBuf;
let mut output: PathBuf;
let working_dir = std::env::current_dir()?;
if let Some(path) = matches.value_of("path") {
let path = Path::new(path);
let path = if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
};
output = path.join("build");
input = path;
} else {
input = match matches.value_of("input") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir.clone(),
};
output = match matches.value_of("output") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir.join("build"),
};
}
if matches.is_present("clean") {
let _ = fs::remove_dir_all(&output);
return Ok(());
}
let client = matches.value_of("client").unwrap();
let server = matches.value_of("server");
let no_building = matches.is_present("no_build");
let only_building = matches.is_present("only_build");
let debug_mode = matches.is_present("debug");
let mut module = open_directory(&input)?;
module.update()?;
if diagnostics::diagnostic(&module)? {
return Ok(());
}
let analyzer_i = &*module.ids_analyzer()?;
let analyzer_ids = analyzer_i.as_ref().map_err(|err| anyhow!("{}", err))?;
let package_name = analyzer_ids.get_package().name();
if !matches.is_present("output") {
output = output.join(&(package_name.to_case(Case::Snake)));
}
let names = module.idl_documents_all_valid_names()?;
let ref_names: Vec<&str> = names.iter().map(|v| v.as_str()).collect();
let analyzers = module.idl_all_analyzers(&ref_names)?;
if !analyzer_ids.has_client(client) {
return Err(anyhow!("Client `{}` not defined", client));
}
let target_client = analyzer_ids.find_client(client).unwrap();
let servers = target_client
.servers(analyzer_ids)
.ok_or(anyhow!("No server specified in client"))?;
if servers.len() == 0 {
return Err(anyhow!("At least one server must be specified"));
}
let target_server = server
.and_then(|name| servers.iter().find(|v| v.ident == name))
.unwrap_or_else(|| servers.first().unwrap());
let mut print_server = false;
let mut print_client = false;
let mut print_client_response = false;
let mut print_server_response = false;
if let Some(print_type) = matches.value_of("print") {
*match print_type {
"client" => &mut print_client,
"client-response" => &mut print_client_response,
"server" => &mut print_server,
"server-response" => &mut print_server_response,
_ => return Err(anyhow!("Invalid value for print argument")),
} = true;
}
if !only_building || print_client || print_client_response {
let target_lang = target_client.language().unwrap();
let request = LanguageRequest {
libraries: get_all_idl_nodes(&analyzers),
ids_nodes: analyzer_ids.nodes.clone(),
request_type: RequestType::Client(ClientType {
client_name: target_client.ident.to_owned(),
server_name: target_server.ident.to_owned(),
}),
};
if print_client {
let request_text = serde_json::to_string_pretty(&request)?;
println!("{}", request_text);
return Ok(());
}
if !print_client_response {
Message::info(format!(
"Sending language `{}` request for client `{}`",
target_lang, client
))?;
}
let gen = Box::new(binary_json::BinaryGen::new(&target_lang)?);
let response = gen.send_request(request)?;
if print_client_response {
let response_text = serde_json::to_string_pretty(&response)?;
println!("{}", response_text);
return Ok(());
}
Message::normal("Response message", response.response_messages)?;
match response.response_type {
ResponseType::Generated(value) => {
let _ = fs::remove_dir_all(&output);
fs::create_dir_all(&output)?;
for item in value {
item.write_items(&output, true)?;
}
Message::info(format!("Generated files at {:#?}", output))?;
}
ResponseType::Undefined(err) => {
Message::error("Request error", err)?;
return Err(anyhow!(""));
}
}
} else if only_building {
Message::warning("This only builds the server side code. This might occur in errors since client generated code was not updated.".to_owned())?;
}
if !no_building || print_server || print_server_response {
let target_lang = target_server.language().unwrap();
let request = LanguageRequest {
libraries: get_all_idl_nodes(&analyzers),
ids_nodes: analyzer_ids.nodes.clone(),
request_type: RequestType::Server(ServerType {
args: ServerArg::Build,
build_type: if debug_mode {
BuildType::Debug
} else {
BuildType::Release
},
server_name: target_server.ident.clone(),
input_path: input.to_str().expect("path error").to_owned(),
}),
};
if print_server {
let request_text = serde_json::to_string_pretty(&request)?;
println!("{}", request_text);
return Ok(());
}
if !print_server_response {
Message::info(format!(
"Sending language `{}` request for building",
target_lang
))?;
}
let gen = Box::new(binary_json::BinaryGen::new(&target_lang)?);
let response = gen.send_request(request)?;
if print_server_response {
let response_text = serde_json::to_string_pretty(&response)?;
println!("{}", response_text);
return Ok(());
}
match response.response_type {
ResponseType::Generated(value) => {
let src = output.join("build").join("idl");
let _ = fs::remove_dir_all(&src); fs::create_dir_all(&src)?;
for item in value {
item.write_items(&src, true)?;
}
Message::info(format!("Generated build files at {:#?}", src))?;
}
ResponseType::Undefined(err) => {
Message::error("Request error", err)?;
return Err(anyhow!(""));
}
}
}
Ok(())
}
}
mod create {
use anyhow::Result;
use clap::{App, Arg, ArgMatches};
use convert_case::{Case, Casing};
use idl::language::*;
use std::path::Path;
use std::{fs, path::PathBuf};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum CreationError {
#[error("Missing project name")]
NameMissing,
#[error("Project `{0}` already exists")]
ProjectExists(String),
}
pub fn create_command<'a>() -> App<'a> {
App::new("create").about("Create a new project").args(&[
Arg::new("output")
.help("Output path")
.short('o')
.long("output")
.takes_value(true),
Arg::new("name").takes_value(true),
])
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
let working_dir = std::env::current_dir()?;
let output: PathBuf = match matches.value_of("output") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir,
};
let name = matches
.value_of("name")
.ok_or(CreationError::NameMissing)?
.to_case(Case::Snake);
let mut output_dir = output.clone();
output_dir.push(&name);
if output_dir.is_dir() {
return Err(CreationError::ProjectExists(name.to_owned()).into());
}
fs::create_dir(output_dir.as_path())?;
create_new_module(&name).write_items(&output, false)?;
Ok(())
}
}
mod diagnostics {
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
term,
term::termcolor::{ColorChoice, StandardStream},
};
use idl::module;
use std::ops::Range;
pub fn diagnostic(module: &module::Module) -> anyhow::Result<bool> {
let files = files::Files::try_from(module).unwrap();
let mut has_errors = false;
let mut messages = vec![];
match module.idl_documents_are_all_valid() {
Ok(_) => {
if let Some(names) = module.idl_documents_names_not_in_package()? {
for name in names {
if let Some(idl_parser) = module.idl_parser(&name) {
match &*idl_parser {
Ok(parser) => {
if let Some(library_name) = parser.library_name() {
if let Some(id) = files.get_id(&name) {
messages.push(Message::PackageWarning {
message: format!(
"Library `{}` not defined in package",
library_name
),
id,
});
}
}
}
Err(_) => {}
}
}
}
}
}
Err(err) => {
messages.push(Message::Package { err });
let idl_parser_errors = module.idl_parser_errors();
let idl_analyze_errors = module.idl_analyze_errors();
let ids_parser_errors = module.ids_parser_errors()?;
let ids_analyze_errors = module.ids_analyze_errors()?;
if let Some((name, err)) = ids_parser_errors {
if let Some(id) = files.get_id(&name) {
let range = err
.get_range()
.get_byte_range(&module.idl_document_text(&name).unwrap_or_default())
.unwrap_or_default();
messages.push(Message::IdsParser { id, err, range });
}
}
if let Some((name, err)) = ids_analyze_errors {
if let Some(id) = files.get_id(&name) {
let range = 0..1;
messages.push(Message::IdsAnalyzer { id, err, range });
}
}
for (name, err) in idl_parser_errors {
if let Some(id) = files.get_id(&name) {
let range = err
.get_range()
.get_byte_range(&module.idl_document_text(&name).unwrap_or_default())
.unwrap_or_default();
messages.push(Message::IdlParser { id, err, range });
}
}
for (name, err) in idl_analyze_errors {
if let Some(id) = files.get_id(&name) {
let range = 0..1;
messages.push(Message::IdlAnalyzer { id, err, range });
}
}
has_errors = !messages.is_empty();
if !has_errors {
if let Some(names) = module.idl_documents_names_not_in_package()? {
for name in names {
if let Some(idl_parser) = module.idl_parser(&name) {
match &*idl_parser {
Ok(parser) => {
if let Some(library_name) = parser.library_name() {
if let Some(id) = files.get_id(&name) {
messages.push(Message::PackageWarning {
message: format!(
"Library `{}` not defined in package",
library_name
),
id,
});
}
}
}
Err(_) => {}
}
}
}
}
}
}
}
if !messages.is_empty() {
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for message in messages {
let writer = &mut writer.lock();
term::emit(writer, &config, &files, &message.to_diagnostic())?;
}
}
Ok(has_errors)
}
mod files {
use codespan_reporting::files;
use std::{collections::HashMap, convert::TryFrom, ops::Range};
#[derive(Debug, Clone)]
struct File {
name: String,
source: String,
line_starts: Vec<usize>,
}
impl File {
fn line_start(&self, line_index: usize) -> Option<usize> {
use std::cmp::Ordering;
match line_index.cmp(&self.line_starts.len()) {
Ordering::Less => {
Some(self.line_starts.get(line_index).expect("failed").clone())
}
Ordering::Equal => Some(self.source.len()),
Ordering::Greater => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct FileId(usize);
#[derive(Debug, Clone)]
pub struct Files {
files: Vec<File>,
names: HashMap<String, FileId>,
}
impl TryFrom<&idl::module::Module> for Files {
type Error = anyhow::Result<()>;
fn try_from(module: &idl::module::Module) -> Result<Self, Self::Error> {
let mut result = Self::new();
for name in module.all_document_names().unwrap() {
match module.idl_document_text(&name) {
Some(source) => {
let _ = result.add(name, source);
}
None => match module.ids_document_text(&name) {
Ok(value) => match value {
Some(source) => {
let _ = result.add(name, source);
}
None => {}
},
Err(_) => {}
},
}
}
Ok(result)
}
}
impl Files {
pub fn get_id(&self, name: &str) -> Option<FileId> {
self.names.get(name).map(|v| *v)
}
fn new() -> Files {
Files {
files: Vec::new(),
names: HashMap::new(),
}
}
fn add(
&mut self,
name: impl Into<String>,
source: impl Into<String>,
) -> Option<FileId> {
let file_id = FileId(usize::try_from(self.files.len()).ok()?);
let name = name.into();
let source = source.into();
let line_starts = files::line_starts(&source).collect();
if self.names.insert(name.to_owned(), file_id).is_some() {
panic!("Same name")
}
self.files.push(File {
name,
line_starts,
source,
});
Some(file_id)
}
fn get(&self, file_id: FileId) -> Option<&File> {
self.files.get(file_id.0 as usize)
}
}
impl<'files> files::Files<'files> for Files {
type FileId = FileId;
type Name = &'files str;
type Source = &'files str;
fn name(&self, file_id: FileId) -> Option<&str> {
Some(self.get(file_id).unwrap().name.as_ref())
}
fn source(&self, file_id: Self::FileId) -> Option<&str> {
Some(&self.get(file_id).unwrap().source)
}
fn line_index(&self, file_id: Self::FileId, byte_index: usize) -> Option<usize> {
Some(
self.get(file_id)?
.line_starts
.binary_search(&byte_index)
.unwrap_or_else(|next_line| next_line - 1),
)
}
fn line_range(&self, file_id: FileId, line_index: usize) -> Option<Range<usize>> {
let file = self.get(file_id)?;
let line_start = file.line_start(line_index)?;
let next_line_start = file.line_start(line_index + 1)?;
Some(line_start..next_line_start)
}
}
}
pub enum Message {
IdlAnalyzer {
id: files::FileId,
range: Range<usize>,
err: idl::idl::analyzer::AnalyzerError,
},
IdlParser {
id: files::FileId,
range: Range<usize>,
err: idl::idl::parser::ParserError,
},
IdsAnalyzer {
id: files::FileId,
range: Range<usize>,
err: idl::ids::analyzer::AnalyzerError,
},
IdsParser {
id: files::FileId,
range: Range<usize>,
err: idl::ids::parser::ParserError,
},
Package {
err: module::PackageModuleError,
},
PackageWarning {
id: files::FileId,
message: String,
},
}
impl Message {
fn to_diagnostic(&self) -> Diagnostic<files::FileId> {
match self {
Message::IdlParser { id, range, err } => {
let (message, _range) = err.get_message_with_range();
Diagnostic::error()
.with_message("parser error")
.with_labels(vec![
Label::primary(*id, range.clone()).with_message(message)
])
}
Message::IdlAnalyzer { id, range, err } => {
let (message, _range) = err.get_message_with_range();
Diagnostic::error()
.with_message("analyzer error")
.with_labels(vec![
Label::primary(*id, range.clone()).with_message(message)
])
}
Message::IdsParser { id, range, err } => {
let (message, _range) = err.get_message_with_range();
Diagnostic::error()
.with_message("parser error")
.with_labels(vec![
Label::primary(*id, range.clone()).with_message(message)
])
}
Message::IdsAnalyzer { id, range, err } => {
let (message, _range) = err.get_message_with_range();
Diagnostic::error()
.with_message("analyzer error")
.with_labels(vec![
Label::primary(*id, range.clone()).with_message(message)
])
}
Message::Package { err } => Diagnostic::error().with_message(err.to_string()),
Message::PackageWarning { message, id } => {
Diagnostic::warning().with_message(message)
}
}
}
}
}
mod env {
use anyhow::Result;
use clap::{App, ArgMatches};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum EnvError {
#[error("Missing env variable")]
VariableMissing,
}
pub fn create_command<'a>() -> App<'a> {
App::new("env").about("Set environment variables")
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
Ok(())
}
}
mod formatter {
use anyhow::Result;
use clap::{App, Arg, ArgMatches};
use idl::module::Module;
pub fn create_command<'a>() -> App<'a> {
App::new("format").about("Format idl files").arg(
Arg::new("file")
.short('f')
.takes_value(true)
.help("Specifies a file instead of the entire project"),
)
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
let mut module = Module::new();
Ok(())
}
}
mod language {
use super::message;
use anyhow::{anyhow, Result};
use clap::{App, Arg, ArgMatches};
use idl::language::protocol_test;
use std::fs;
use std::path::PathBuf;
use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum LanguageError {
#[error("Project already exists")]
ProjectExists(),
}
pub fn create_command<'a>() -> App<'a> {
App::new("language")
.about("Language implementations")
.subcommand(
App::new("test").about("Protocol testing").args(&[
Arg::new("generate_files")
.help("Generate idl library for testing")
.long("generate-files"),
Arg::new("output")
.help("Output path")
.short('o')
.long("output")
.takes_value(true),
]),
)
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
match matches.subcommand() {
Some(("test", value)) => args_testing(value),
_ => Err(anyhow!("")),
}
}
fn args_testing(matches: &ArgMatches) -> Result<()> {
let working_dir = std::env::current_dir()?;
let output: PathBuf = match matches.value_of("output") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir,
};
if matches.is_present("generate_files") {
let name = "idl_protocol_test";
let mut output_dir = output.clone();
output_dir.push(&name);
if output_dir.is_dir() {
protocol_test::create_new_module_for_test().write_items(&output, true)?;
} else {
fs::create_dir(output_dir.as_path())?;
protocol_test::create_new_module_for_test().write_items(&output, false)?;
}
return Ok(());
}
Ok(())
}
}
mod message {
use ansi_term::Color;
pub struct Message;
impl Message {
pub fn error(name: &str, message: String) -> anyhow::Result<()> {
if !message.is_empty() {
if !name.is_empty() {
println!("{}: ", Color::White.paint(name));
}
println!("{}", Color::Red.paint(message));
}
Ok(())
}
pub fn normal(name: &str, messages: Vec<String>) -> anyhow::Result<()> {
if !messages.is_empty() {
if !name.is_empty() {
println!("{}: ", Color::White.paint(name));
}
for message in messages {
println!("{}", Color::Blue.paint(message));
}
}
Ok(())
}
pub fn info(message: String) -> anyhow::Result<()> {
if !message.is_empty() {
println!("{}", Color::White.paint(message));
}
Ok(())
}
pub fn warning(message: String) -> anyhow::Result<()> {
if !message.is_empty() {
println!("{}", Color::Yellow.paint(message));
}
Ok(())
}
}
}
mod server {
use std::{
fs,
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
process::Command,
};
use super::message::Message;
use idl::builder::{binary_json, get_all_idl_nodes, open_directory};
use tempfile::tempdir;
use super::diagnostics;
use anyhow::{anyhow, Result};
use clap::{App, Arg, ArgMatches};
use convert_case::{Case, Casing};
use idl::language::*;
pub fn create_command<'a>() -> App<'a> {
App::new("server")
.about("Generate server files for implementation")
.args(&[
Arg::new("output")
.help("Output path")
.short('o')
.long("output")
.takes_value(true),
Arg::new("input")
.help("Idl path")
.short('i')
.long("input")
.conflicts_with("path")
.takes_value(true),
Arg::new("path")
.help("The target directory")
.long("path")
.takes_value(true)
.conflicts_with_all(&["input", "output"]),
Arg::new("server")
.help("Server name")
.long("server")
.takes_value(true),
Arg::new("print")
.help("Prints the request in a JSON format")
.long("print")
.takes_value(false),
])
.subcommand(
App::new("run").about("Compiles and run the server").args(&[
Arg::new("output")
.help("Output path")
.short('o')
.long("output")
.takes_value(true),
Arg::new("input")
.help("Idl path")
.short('i')
.long("input")
.conflicts_with("path")
.takes_value(true),
Arg::new("path")
.help("The target directory")
.long("path")
.takes_value(true)
.conflicts_with_all(&["input", "output"]),
Arg::new("server")
.help("Server name")
.long("server")
.takes_value(true),
Arg::new("debug")
.help("Debug mode")
.long("debug")
.takes_value(false)
.conflicts_with_all(&["clean, no_build"]),
Arg::new("clean")
.help("Remove all generated files")
.long("clean")
.conflicts_with("output")
.takes_value(false)
.conflicts_with("no_build"),
]),
)
}
pub fn parse(matches: &ArgMatches) -> Result<()> {
match matches.subcommand() {
Some(("run", value)) => run_server(value),
_ => args_server(matches),
}
}
fn get_paths(matches: &ArgMatches) -> Result<(PathBuf, PathBuf)> {
let input: PathBuf;
let output: PathBuf;
let working_dir = std::env::current_dir()?;
if let Some(path) = matches.value_of("path") {
let path = Path::new(path);
let path = if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
};
input = path.clone();
output = path;
} else {
input = match matches.value_of("input") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir.clone(),
};
output = match matches.value_of("output") {
Some(value) => {
let path = Path::new(value);
if path.is_relative() {
working_dir.join(path)
} else {
path.to_path_buf()
}
}
None => working_dir,
};
}
Ok((input, output))
}
fn args_server(matches: &ArgMatches) -> Result<()> {
let server = matches.value_of("server");
let (input, mut output) = get_paths(matches)?;
let mut module = open_directory(&input)?;
module.update()?;
if diagnostics::diagnostic(&module)? {
return Ok(());
}
let package_name = module.package_name()?;
let analyzer_i = &*module.ids_analyzer()?;
let analyzer_ids = analyzer_i.as_ref().map_err(|err| anyhow!("{}", err))?;
let server = match server {
Some(server) => analyzer_ids
.find_server(server)
.ok_or_else(|| anyhow!("Undefined server `{}`", server))?,
None => {
let n_servers = analyzer_ids
.nodes
.iter()
.filter_map(|v| match v {
idl::ids::nodes::IdsNode::Server(value) => Some(value),
_ => None,
})
.count();
if n_servers > 1 {
return Err(anyhow!("More than one server were specified in package"));
} else if n_servers == 0 {
return Err(anyhow!("No server defined in package"));
} else {
analyzer_ids
.nodes
.iter()
.find_map(|v| match v {
idl::ids::nodes::IdsNode::Server(value) => Some(value),
_ => None,
})
.unwrap()
}
}
};
let language = server
.get_field("language")
.ok_or_else(|| anyhow!("Missing languange field"))?
.as_string_value()
.ok_or_else(|| anyhow!("Invalid field for language"))?;
if !matches.is_present("output") {
output = output
.join(&(language.to_case(Case::Snake)))
.join(&(package_name.to_case(Case::Snake)));
}
let names = module.idl_documents_all_valid_names()?;
let ref_names: Vec<&str> = names.iter().map(|v| v.as_str()).collect();
let analyzers = module.idl_all_analyzers(&ref_names)?;
let request = LanguageRequest {
libraries: get_all_idl_nodes(&analyzers),
ids_nodes: analyzer_ids.nodes.clone(),
request_type: RequestType::Server(ServerType {
server_name: server.ident.to_owned(),
input_path: input.to_str().expect("path error").to_owned(),
args: ServerArg::Generate,
build_type: BuildType::Release,
}),
};
if matches.is_present("print") {
let request_text = serde_json::to_string_pretty(&request)?;
println!("{}", request_text);
return Ok(());
}
let gen = Box::new(binary_json::BinaryGen::new(&language)?);
let response = gen.send_request(request)?;
match response.response_type {
ResponseType::Generated(value) => {
fs::create_dir_all(&output)?;
for item in value {
item.write_items(&output, false)?;
}
Message::info(format!("Generated files at {:#?}", output))?;
}
ResponseType::Undefined(err) => return Err(anyhow!("Response error `{}`", err)),
}
Ok(())
}
fn run_server(matches: &ArgMatches) -> Result<()> {
let server = matches.value_of("server");
let debug_mode = matches.is_present("debug");
let (input, _) = get_paths(matches)?;
let mut module = open_directory(&input)?;
module.update()?;
if diagnostics::diagnostic(&module)? {
return Ok(());
}
let package_name = module.package_name()?;
let analyzer_i = &*module.ids_analyzer()?;
let analyzer_ids = analyzer_i.as_ref().map_err(|err| anyhow!("{}", err))?;
let server = match server {
Some(server) => analyzer_ids
.find_server(server)
.ok_or_else(|| anyhow!("Undefined server `{}`", server))?,
None => {
let n_servers = analyzer_ids
.nodes
.iter()
.filter_map(|v| match v {
idl::ids::nodes::IdsNode::Server(value) => Some(value),
_ => None,
})
.count();
if n_servers > 1 {
return Err(anyhow!("More than one server were specified in package"));
} else if n_servers == 0 {
return Err(anyhow!("No servers defined in package"));
} else {
analyzer_ids
.nodes
.iter()
.find_map(|v| match v {
idl::ids::nodes::IdsNode::Server(value) => Some(value),
_ => None,
})
.unwrap()
}
}
};
let language = server
.get_field("language")
.ok_or_else(|| anyhow!("Missing languange field"))?
.as_string_value()
.ok_or_else(|| anyhow!(""))?;
let names = module.idl_documents_all_valid_names()?;
let ref_names: Vec<&str> = names.iter().map(|v| v.as_str()).collect();
let analyzers = module.idl_all_analyzers(&ref_names)?;
let request = LanguageRequest {
libraries: get_all_idl_nodes(&analyzers),
ids_nodes: analyzer_ids.nodes.clone(),
request_type: RequestType::Server(ServerType {
server_name: server.ident.to_owned(),
build_type: if debug_mode {
BuildType::Debug
} else {
BuildType::Release
},
input_path: input.to_str().expect("path error").to_owned(),
args: ServerArg::Run,
}),
};
let gen = Box::new(binary_json::BinaryGen::new(&language)?);
let response = gen.send_request(request)?;
match response.response_type {
ResponseType::Generated(value) => {
let dir = tempdir()?;
let path = dir.path();
fs::create_dir_all(&path)?;
for item in value {
item.write_items(&path, false)?;
}
let item = path.read_dir()?.nth(0).unwrap()?;
let path = item.path();
fs::set_permissions(path.clone(), fs::Permissions::from_mode(0o100))?;
let path_name = path.to_str().unwrap();
let mut cmd = Command::new(path_name);
let mut child = cmd.spawn().expect(&format!(
"Failed to run {}",
package_name.to_case(Case::Snake)
));
Message::info(format!("Executing {} ", package_name))?;
let exit_code = child.wait()?;
Message::info(format!(
"Finished executing {} with exit code: {}",
package_name, exit_code
))?;
dir.close()?;
}
ResponseType::Undefined(err) => return Err(anyhow!("Response error `{}`", err)),
}
Ok(())
}
}
fn main() -> anyhow::Result<()> {
let mut app = App::new("idl")
.color(clap::ColorChoice::Never)
.version("0.1")
.author("Adriano Souza <adriano.souza113@gmail.com>")
.about("IDL language")
.subcommand(analyze::create_command())
.subcommand(client::create_command())
.subcommand(server::create_command())
.subcommand(clean::create_command())
.subcommand(create::create_command())
.subcommand(formatter::create_command())
.subcommand(language::create_command())
.subcommand(env::create_command());
let matches = app.get_matches_mut();
if let Err(err) = match matches.subcommand() {
Some(("format", value)) => formatter::parse(value),
Some(("server", value)) => server::parse(value),
Some(("client", value)) => client::parse(value),
Some(("clean", value)) => clean::parse(value),
Some(("create", value)) => create::parse(value),
Some(("analyze", value)) => analyze::parse(value),
Some(("language", value)) => language::parse(value),
Some(("env", value)) => env::parse(value),
_ => {
message::Message::info(app.render_usage())?;
Ok(())
}
} {
message::Message::error("", err.to_string())?;
}
Ok(())
}