diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cli.rs | 20 | ||||
-rw-r--r-- | src/config.rs | 107 | ||||
-rw-r--r-- | src/main.rs | 72 |
3 files changed, 196 insertions, 3 deletions
@@ -1,5 +1,9 @@ | |||
1 | use crate::config::Config; | ||
2 | |||
1 | use structopt::StructOpt; | 3 | use structopt::StructOpt; |
2 | 4 | ||
5 | use std::path::PathBuf; | ||
6 | |||
3 | #[derive(Debug, Clone, StructOpt)] | 7 | #[derive(Debug, Clone, StructOpt)] |
4 | pub(crate) struct Cli { | 8 | pub(crate) struct Cli { |
5 | #[structopt(long, short, parse(from_occurrences))] | 9 | #[structopt(long, short, parse(from_occurrences))] |
@@ -8,11 +12,27 @@ pub(crate) struct Cli { | |||
8 | #[structopt(long, number_of_values = 1, parse(try_from_str = shell_words::split))] | 12 | #[structopt(long, number_of_values = 1, parse(try_from_str = shell_words::split))] |
9 | pub(crate) extra_arg: Vec<Vec<String>>, | 13 | pub(crate) extra_arg: Vec<Vec<String>>, |
10 | 14 | ||
15 | #[structopt(long, short = "C", default_value = ".")] | ||
16 | pub(crate) directory: PathBuf, | ||
17 | |||
11 | #[structopt(subcommand)] | 18 | #[structopt(subcommand)] |
12 | pub(crate) command: Command, | 19 | pub(crate) command: Command, |
20 | |||
21 | #[structopt(flatten)] | ||
22 | pub(crate) common_options: Config, | ||
13 | } | 23 | } |
14 | 24 | ||
15 | #[derive(Debug, Clone, StructOpt)] | 25 | #[derive(Debug, Clone, StructOpt)] |
16 | pub(crate) enum Command { | 26 | pub(crate) enum Command { |
17 | Generate { file: String }, | 27 | Generate { file: String }, |
28 | Config { | ||
29 | #[structopt(subcommand)] | ||
30 | command: ConfigCommand, | ||
31 | } | ||
32 | } | ||
33 | |||
34 | #[derive(Debug, Clone, StructOpt)] | ||
35 | pub(crate) enum ConfigCommand { | ||
36 | Default, | ||
37 | Show, | ||
18 | } | 38 | } |
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..4463479 --- /dev/null +++ b/src/config.rs | |||
@@ -0,0 +1,107 @@ | |||
1 | use anyhow::{anyhow, Context, Result}; | ||
2 | use codemap::CodeMap; | ||
3 | use codemap_diagnostic::Diagnostic; | ||
4 | use serde_derive::{Deserialize, Serialize}; | ||
5 | use structopt::StructOpt; | ||
6 | use thiserror::Error; | ||
7 | |||
8 | use std::path::{Path, PathBuf}; | ||
9 | use std::sync::Arc; | ||
10 | |||
11 | pub(super) const DEFAULT_PROJECT_CONFIGURATION_FILE_NAME: &str = "poseidoc.toml"; | ||
12 | |||
13 | #[derive(Debug, Clone, StructOpt, Deserialize, Serialize)] | ||
14 | #[structopt(rename_all = "kebab-case")] | ||
15 | #[serde(rename_all = "kebab-case", default)] | ||
16 | pub(crate) struct Config { | ||
17 | #[structopt(skip)] | ||
18 | pub(crate) name: String, | ||
19 | #[structopt(long, default_value = ".")] | ||
20 | pub(crate) compile_commands_location: PathBuf, | ||
21 | #[structopt(skip = vec![])] | ||
22 | pub(crate) extra_clang_args: Vec<String>, | ||
23 | } | ||
24 | |||
25 | impl Default for Config { | ||
26 | fn default() -> Self { | ||
27 | Config { | ||
28 | name: "My Project".into(), | ||
29 | compile_commands_location: PathBuf::from(r"."), | ||
30 | extra_clang_args: Vec::new(), | ||
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | pub(super) fn load_config(location: impl AsRef<Path>, codemap: &mut CodeMap) -> Result<Config> { | ||
36 | let location = location.as_ref(); | ||
37 | |||
38 | let final_path = if location.is_dir() { | ||
39 | location.join(DEFAULT_PROJECT_CONFIGURATION_FILE_NAME) | ||
40 | } else if !location.exists() { | ||
41 | return Err(anyhow!("File {:?} does not exists", location)) | ||
42 | .with_context(|| format!("Failed to open project configuration: {:?}", location)); | ||
43 | } else { | ||
44 | location.to_owned() | ||
45 | }; | ||
46 | |||
47 | let config_str = | ||
48 | std::fs::read_to_string(&final_path).map_err(|err| ConfigLoadError::IoError { | ||
49 | file_name: final_path.clone(), | ||
50 | source: err, | ||
51 | })?; | ||
52 | Ok( | ||
53 | toml::from_str(&config_str).map_err(|err| ConfigLoadError::TomlDeserialzation { | ||
54 | file: codemap.add_file(final_path.to_string_lossy().into(), config_str), | ||
55 | source: err, | ||
56 | })?, | ||
57 | ) | ||
58 | } | ||
59 | |||
60 | #[derive(Debug, Error)] | ||
61 | #[error("Error loading project configuration")] | ||
62 | pub(crate) enum ConfigLoadError { | ||
63 | #[error("Failed to read project configuration: {:?}", file_name)] | ||
64 | IoError { | ||
65 | file_name: PathBuf, | ||
66 | source: std::io::Error, | ||
67 | }, | ||
68 | #[error("Failed to parse project configuration: {:?}", file.name())] | ||
69 | TomlDeserialzation { | ||
70 | file: Arc<codemap::File>, | ||
71 | source: toml::de::Error, | ||
72 | }, | ||
73 | } | ||
74 | |||
75 | impl From<&ConfigLoadError> for Diagnostic { | ||
76 | fn from(err: &ConfigLoadError) -> Diagnostic { | ||
77 | use codemap_diagnostic::{Level, SpanLabel, SpanStyle}; | ||
78 | use std::convert::TryInto; | ||
79 | |||
80 | let message = err.to_string(); | ||
81 | |||
82 | let spans = match err { | ||
83 | ConfigLoadError::IoError { .. } => vec![], | ||
84 | ConfigLoadError::TomlDeserialzation { file, source, .. } => { | ||
85 | if let Some((line, col)) = source.line_col() { | ||
86 | let line_span = file.line_span(line); | ||
87 | let col = col.try_into().unwrap(); | ||
88 | let span = line_span.subspan(col, col); | ||
89 | vec![SpanLabel { | ||
90 | span, | ||
91 | label: None, | ||
92 | style: SpanStyle::Primary, | ||
93 | }] | ||
94 | } else { | ||
95 | vec![] | ||
96 | } | ||
97 | } | ||
98 | }; | ||
99 | |||
100 | Diagnostic { | ||
101 | level: Level::Error, | ||
102 | message, | ||
103 | code: None, | ||
104 | spans, | ||
105 | } | ||
106 | } | ||
107 | } | ||
diff --git a/src/main.rs b/src/main.rs index e1d4752..503688e 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //mod doxygen; | 1 | //mod doxygen; |
2 | mod cli; | 2 | mod cli; |
3 | mod config; | ||
3 | mod entities; | 4 | mod entities; |
4 | mod generator; | 5 | mod generator; |
5 | mod pandoc; | 6 | mod pandoc; |
@@ -8,14 +9,63 @@ mod parsing; | |||
8 | #[macro_use] | 9 | #[macro_use] |
9 | extern crate log; | 10 | extern crate log; |
10 | 11 | ||
11 | use cli::Command; | 12 | use cli::{Command, ConfigCommand}; |
12 | use generator::generate; | 13 | use generator::generate; |
13 | use parsing::parse_file; | 14 | use parsing::parse_file; |
14 | 15 | ||
15 | use anyhow::Result; | 16 | use anyhow::{Context, Result}; |
17 | use codemap::CodeMap; | ||
18 | use codemap_diagnostic::{Diagnostic, Emitter, Level}; | ||
16 | use structopt::StructOpt; | 19 | use structopt::StructOpt; |
17 | 20 | ||
18 | fn main() -> Result<()> { | 21 | fn into_diagnostic(err: &anyhow::Error) -> Diagnostic { |
22 | if let Some(err) = err.downcast_ref::<config::ConfigLoadError>() { | ||
23 | err.into() | ||
24 | } else { | ||
25 | Diagnostic { | ||
26 | level: Level::Error, | ||
27 | message: err.to_string(), | ||
28 | code: None, | ||
29 | spans: vec![], | ||
30 | } | ||
31 | } | ||
32 | } | ||
33 | |||
34 | fn main() { | ||
35 | let mut codemap = CodeMap::new(); | ||
36 | |||
37 | match start(&mut codemap) { | ||
38 | Ok(()) => {} | ||
39 | Err(err) => { | ||
40 | let mut emitter = | ||
41 | Emitter::stderr(codemap_diagnostic::ColorConfig::Auto, Some(&codemap)); | ||
42 | |||
43 | let mut diagnostics = Vec::new(); | ||
44 | diagnostics.push(into_diagnostic(&err)); | ||
45 | |||
46 | if let Some(mut source) = err.source() { | ||
47 | diagnostics.push(Diagnostic { | ||
48 | level: Level::Note, | ||
49 | message: format!("Caused by: {}", source), | ||
50 | code: None, | ||
51 | spans: vec![], | ||
52 | }); | ||
53 | while let Some(source2) = source.source() { | ||
54 | source = source2; | ||
55 | diagnostics.push(Diagnostic { | ||
56 | level: Level::Note, | ||
57 | message: format!("Caused by: {}", source), | ||
58 | code: None, | ||
59 | spans: vec![], | ||
60 | }); | ||
61 | } | ||
62 | } | ||
63 | emitter.emit(&diagnostics); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | fn start(codemap: &mut CodeMap) -> Result<()> { | ||
19 | let cli = cli::Cli::from_args(); | 69 | let cli = cli::Cli::from_args(); |
20 | pretty_env_logger::formatted_builder() | 70 | pretty_env_logger::formatted_builder() |
21 | .filter( | 71 | .filter( |
@@ -30,6 +80,9 @@ fn main() -> Result<()> { | |||
30 | ) | 80 | ) |
31 | .try_init()?; | 81 | .try_init()?; |
32 | 82 | ||
83 | std::env::set_current_dir(&cli.directory) | ||
84 | .with_context(|| format!("Cannot change current directory to: {:?}", cli.directory))?; | ||
85 | |||
33 | match cli.command { | 86 | match cli.command { |
34 | Command::Generate { file } => { | 87 | Command::Generate { file } => { |
35 | let extra_args = cli.extra_arg.iter().flatten().map(AsRef::as_ref).collect(); | 88 | let extra_args = cli.extra_arg.iter().flatten().map(AsRef::as_ref).collect(); |
@@ -38,6 +91,19 @@ fn main() -> Result<()> { | |||
38 | let base_output_dir = std::path::Path::new("doc"); | 91 | let base_output_dir = std::path::Path::new("doc"); |
39 | generate(&base_output_dir, manager)?; | 92 | generate(&base_output_dir, manager)?; |
40 | } | 93 | } |
94 | Command::Config { | ||
95 | command: ConfigCommand::Default, | ||
96 | } => { | ||
97 | print!("{}", toml::to_string_pretty(&config::Config::default())?); | ||
98 | } | ||
99 | Command::Config { | ||
100 | command: ConfigCommand::Show, | ||
101 | } => { | ||
102 | print!( | ||
103 | "{}", | ||
104 | toml::to_string_pretty(&config::load_config(".", codemap)?)? | ||
105 | ); | ||
106 | } | ||
41 | } | 107 | } |
42 | 108 | ||
43 | Ok(()) | 109 | Ok(()) |