char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somexperiment: automatic types for npm packages51266f9

main
3.4 KiB116 linesraw
1use std::path::PathBuf;
2use std::process::ExitCode;
3
4use crate::cli::ParseError;
5use crate::fragments::{Arg, Entry, Parser, reject};
6
7pub struct Args {
8    pub typescript: PathBuf,
9    pub entry: Entry,
10}
11
12/// `avril check` argument parsing failed.
13#[derive(Debug, thiserror::Error)]
14pub enum CheckParseError {
15    #[error(transparent)]
16    Generic(#[from] ParseError),
17    #[error("check requires --typescript=PATH to the native backend")]
18    MissingBackend,
19}
20
21pub fn parse(args: &[String]) -> Result<Args, CheckParseError> {
22    let cwd = crate::fragments::working_dir()?;
23    let mut parser = Parser::new("check", args.to_vec(), &cwd)?;
24    let (mut typescript, mut raw) = (None, None);
25    while let Some(arg) = parser.next()? {
26        match arg {
27            Arg::Positional(arg) if raw.is_none() => raw = Some(arg),
28            Arg::Positional(_) => return Err(parser.usage().into()),
29            Arg::Flag(arg) if parser.path_once(&arg, "--typescript", &mut typescript)? => {}
30            Arg::Flag(arg) => return Err(reject(&arg, &[]).into()),
31        }
32    }
33    let typescript = typescript.ok_or(CheckParseError::MissingBackend)?;
34    let raw = raw.ok_or_else(|| parser.usage())?;
35    let entry = parser.entry(&raw)?;
36    Ok(Args {
37        typescript: PathBuf::from(typescript),
38        entry,
39    })
40}
41
42fn display_uri(uri: &str) -> String {
43    if let Some(path) = url::Url::parse(uri)
44        .ok()
45        .and_then(|url| url.to_file_path().ok())
46    {
47        return path.display().to_string();
48    }
49    for (editor, original) in [
50        ("avril-https:", "https:"),
51        ("avril-http:", "http:"),
52        ("avril-builtin:", "avril:"),
53    ] {
54        if let Some(rest) = uri.strip_prefix(editor) {
55            return format!("{original}{rest}");
56        }
57    }
58    uri.to_owned()
59}
60
61pub fn run(args: Args) -> Result<ExitCode, Box<dyn std::error::Error>> {
62    let Args { typescript, entry } = args;
63    let (_authority, root, packages) = entry.project(false)?;
64    let runtime = tokio::runtime::Builder::new_multi_thread()
65        .worker_threads(2)
66        .enable_all()
67        .build()?;
68    let caps = entry.caps;
69    let explicit_lock = entry.explicit_lock;
70    let module = entry.module;
71    let diagnostics = runtime.block_on(async move {
72        let input = crate::lsp::check_input(
73            root,
74            caps,
75            packages,
76            explicit_lock,
77            entry.automatic_types,
78            &module,
79        )
80        .await?;
81        avril_lsp::check(&typescript, input).await
82    });
83    let diagnostics = match diagnostics {
84        Ok(diagnostics) => diagnostics,
85        Err(error) => {
86            runtime.shutdown_background();
87            return Err(error);
88        }
89    };
90    runtime.shutdown_background();
91    let mut failed = false;
92    for diagnostic in &diagnostics {
93        let severity = if diagnostic.severity == 1 {
94            failed = true;
95            "error"
96        } else {
97            "warning"
98        };
99        let code = diagnostic
100            .code
101            .map(|code| format!(" TS{code}"))
102            .unwrap_or_default();
103        println!(
104            "{}:{}:{}: {severity}{code}: {}",
105            display_uri(&diagnostic.uri),
106            diagnostic.line + 1,
107            diagnostic.character + 1,
108            diagnostic.message
109        );
110    }
111    Ok(if failed {
112        ExitCode::FAILURE
113    } else {
114        ExitCode::SUCCESS
115    })
116}