feat(backend): extend cached resolution for go

This commit is contained in:
Ruben Fiszel
2023-04-09 10:57:42 +02:00
parent 130ddbbf85
commit 36e338c461
10 changed files with 236 additions and 2763 deletions
+51
View File
@@ -1396,6 +1396,16 @@ dependencies = [
"url",
]
[[package]]
name = "gosyn"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1584c4cbcb6f1d97481c19d1951c64f6e1256e5a03c28159ae45431c00511ee7"
dependencies = [
"strum",
"unic-ucd-category",
]
[[package]]
name = "h2"
version = "0.3.16"
@@ -2014,6 +2024,12 @@ dependencies = [
"regex-automata",
]
[[package]]
name = "matches"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "matchit"
version = "0.7.0"
@@ -3720,6 +3736,28 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.109",
]
[[package]]
name = "subtle"
version = "2.4.1"
@@ -4426,6 +4464,18 @@ dependencies = [
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-category"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0"
dependencies = [
"matches",
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-ident"
version = "0.9.0"
@@ -4974,6 +5024,7 @@ name = "windmill-parser-go"
version = "1.86.0"
dependencies = [
"anyhow",
"gosyn",
"itertools",
"phf",
"unicode-general-category",
+3 -6
View File
@@ -28,11 +28,7 @@ name = "windmill"
path = "./src/main.rs"
[features]
enterprise = [
"windmill-worker/enterprise",
"windmill-queue/enterprise",
"windmill-api/enterprise",
]
enterprise = ["windmill-worker/enterprise", "windmill-queue/enterprise", "windmill-api/enterprise"]
[dependencies]
anyhow.workspace = true
@@ -154,4 +150,5 @@ async-stripe = { version = "0.14", features = [
"checkout",
] }
async_zip = { version = "0.0.11", features = ["full"] }
once_cell = "1.17.1"
once_cell = "1.17.1"
gosyn = "0.2.2"
@@ -15,3 +15,4 @@ phf.workspace = true
unicode-general-category.workspace = true
itertools.workspace = true
anyhow.workspace = true
gosyn.workspace = true
File diff suppressed because it is too large Load Diff
@@ -1,347 +0,0 @@
#![allow(clippy::large_enum_variant)] // TODO: we allow large enum variant for now, let's profile properly to see if we want to box.
use crate::parser_go_token::{Position, Token};
use std::collections::BTreeMap;
// https://pkg.go.dev/go/ast#CommentGroup
#[derive(Debug)]
pub struct CommentGroup {
// List []*Comment // len(List) > 0
}
// https://pkg.go.dev/go/ast#FieldList
#[derive(Debug)]
pub struct FieldList<'a> {
pub opening: Option<Position<'a>>, // position of opening parenthesis/brace, if any
pub list: Vec<Field<'a>>, // field list; or nil
pub closing: Option<Position<'a>>, // position of closing parenthesis/brace, if any
}
// https://pkg.go.dev/go/ast#Field
#[derive(Debug)]
pub struct Field<'a> {
pub doc: Option<CommentGroup>, // associated documentation; or nil
pub names: Option<Vec<Ident<'a>>>, // field/method/(type) parameter names, or type "type"; or nil
pub type_: Option<Expr<'a>>, // field/method/parameter type, type list type; or nil
pub tag: Option<BasicLit<'a>>, // field tag; or nil
pub comment: Option<CommentGroup>, // line comments; or nil
}
// https://pkg.go.dev/go/ast#File
#[derive(Debug)]
pub struct File<'a> {
// package name
pub decls: Vec<Decl<'a>>, // top-level declarations; or nil // list of all comments in the source file
}
// https://pkg.go.dev/go/ast#FuncDecl
#[derive(Debug)]
pub struct FuncDecl<'a> {
pub doc: Option<CommentGroup>, // associated documentation; or nil
pub recv: Option<FieldList<'a>>, // receiver (methods); or nil (functions)
pub name: Ident<'a>, // function/method name
pub type_: FuncType<'a>, // function signature: type and value parameters, results, and position of "func" keyword
pub body: Option<BlockStmt<'a>>, // function body; or nil for external (non-Go) function
}
// https://pkg.go.dev/go/ast#BlockStmt
#[derive(Debug)]
pub struct BlockStmt<'a> {
pub lbrace: Position<'a>, // position of "{"
pub list: Vec<Stmt>,
pub rbrace: Position<'a>, // position of "}", if any (may be absent due to syntax error)
}
// https://pkg.go.dev/go/ast#FuncType
#[derive(Debug)]
pub struct FuncType<'a> {
pub func: Option<Position<'a>>, // position of "func" keyword (token.NoPos if there is no "func")
pub params: FieldList<'a>, // (incoming) parameters; non-nil
pub results: Option<FieldList<'a>>, // (outgoing) results; or nil
}
// https://pkg.go.dev/go/ast#Ident
#[derive(Debug)]
pub struct Ident<'a> {
pub name_pos: Position<'a>, // identifier position
pub name: &'a str, // identifier name
pub obj: Option<Box<Object<'a>>>, // denoted object; or nil
}
// https://pkg.go.dev/go/ast#ValueSpec
#[derive(Debug)]
pub struct ValueSpec<'a> {
pub doc: Option<CommentGroup>, // associated documentation; or nil
pub names: Vec<Ident<'a>>, // value names (len(Names) > 0)
pub type_: Option<Expr<'a>>, // value type; or nil
pub values: Option<Vec<Expr<'a>>>, // initial values; or nil
pub comment: Option<CommentGroup>, // line comments; or nil
}
// https://pkg.go.dev/go/ast#BasicLit
#[derive(Debug)]
pub struct BasicLit<'a> {
pub value_pos: Position<'a>, // literal position
pub kind: Token, // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
pub value: &'a str, // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}
// https://pkg.go.dev/go/ast#Object
#[derive(Debug)]
pub struct Object<'a> {
pub kind: ObjKind,
pub name: &'a str, // declared name
pub decl: Option<ObjDecl>, // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
pub data: Option<usize>, // object-specific data; or nil
pub type_: Option<()>, // placeholder for type information; may be nil
}
// https://pkg.go.dev/go/ast#Ellipsis
#[derive(Debug)]
pub struct Ellipsis<'a> {
pub ellipsis: Position<'a>, // position of "..."
pub elt: Option<Box<Expr<'a>>>, // ellipsis element type (parameter lists only); or nil
}
// https://pkg.go.dev/go/ast#Ellipsis
#[derive(Debug)]
pub struct TypeAssertExpr<'a> {
pub x: Box<Expr<'a>>, // expression
pub lparen: Position<'a>, // position of "("
pub type_: Box<Expr<'a>>, // asserted type; nil means type switch X.(type)
pub rparen: Position<'a>, // position of ")"
}
// https://pkg.go.dev/go/ast#SliceExpr
#[derive(Debug)]
pub struct SliceExpr<'a> {
pub x: Box<Expr<'a>>, // expression
pub lbrack: Position<'a>, // position of "["
pub low: Option<Box<Expr<'a>>>, // begin of slice range; or nil
pub high: Option<Box<Expr<'a>>>, // end of slice range; or nil
pub max: Option<Box<Expr<'a>>>, // maximum capacity of slice; or nil
pub slice3: bool, // true if 3-index slice (2 colons present)
pub rbrack: Position<'a>, // position of "]"
}
// https://pkg.go.dev/go/ast#ObjKind
#[derive(Debug)]
pub enum ObjKind {}
#[derive(Debug)]
pub enum ObjDecl {}
// https://pkg.go.dev/go/ast#Decl
#[derive(Debug)]
pub enum Decl<'a> {
FuncDecl(FuncDecl<'a>),
}
// https://pkg.go.dev/go/ast#Scope
#[derive(Debug)]
pub struct Scope<'a> {
pub outer: Option<Box<Scope<'a>>>,
pub objects: BTreeMap<&'a str, Object<'a>>,
}
// https://pkg.go.dev/go/ast#GenDecl
#[derive(Debug)]
pub struct GenDecl<'a> {
pub doc: Option<CommentGroup>, // associated documentation; or nil
pub tok_pos: Position<'a>, // position of Tok
pub tok: Token, // IMPORT, CONST, TYPE, or VAR
pub lparen: Option<Position<'a>>, // position of '(', if any
pub specs: Vec<Spec>,
pub rparen: Option<Position<'a>>, // position of ')', if any
}
// https://pkg.go.dev/go/ast#AssignStmt
#[derive(Debug)]
pub struct AssignStmt<'a> {
pub lhs: Vec<Expr<'a>>,
pub tok_pos: Position<'a>, // position of Tok
pub tok: Token, // assignment token, DEFINE
pub rhs: Vec<Expr<'a>>,
}
// https://pkg.go.dev/go/ast#BinaryExpr
#[derive(Debug)]
pub struct BinaryExpr<'a> {
pub x: Box<Expr<'a>>, // left operand
pub op_pos: Position<'a>, // position of Op
pub op: Token, // operator
pub y: Box<Expr<'a>>, // right operand
}
// https://pkg.go.dev/go/ast#ReturnStmt
#[derive(Debug)]
pub struct ReturnStmt<'a> {
pub return_: Position<'a>, // position of "return" keyword
pub results: Vec<Expr<'a>>, // result expressions; or nil
}
// https://pkg.go.dev/go/ast#TypeSpec
#[derive(Debug)]
pub struct TypeSpec<'a> {
pub doc: Option<CommentGroup>, // associated documentation; or nil
pub name: Option<Ident<'a>>, // type name
pub assign: Option<Position<'a>>, // position of '=', if any
pub type_: Expr<'a>, // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
pub comment: Option<CommentGroup>, // line comments; or nil
}
// https://pkg.go.dev/go/ast#StructType
#[derive(Debug)]
pub struct StructType<'a> {
pub struct_: Position<'a>, // position of "struct" keyword
pub fields: Option<FieldList<'a>>, // list of field declarations
pub incomplete: bool, // true if (source) fields are missing in the Fields list
}
// https://pkg.go.dev/go/ast#StarExpr
#[derive(Debug)]
pub struct StarExpr<'a> {
pub star: Position<'a>, // position of "*"
pub x: Box<Expr<'a>>, // operand
}
// https://pkg.go.dev/go/ast#InterfaceType
#[derive(Debug)]
pub struct InterfaceType<'a> {
pub interface: Position<'a>, // position of "interface" keyword
pub methods: Option<FieldList<'a>>, // list of embedded interfaces, methods, or types
pub incomplete: bool, // true if (source) methods or types are missing in the Methods list
}
// https://pkg.go.dev/go/ast#UnaryExpr
#[derive(Debug)]
pub struct UnaryExpr<'a> {
pub op_pos: Position<'a>, // position of Op
pub op: Token, // operator
pub x: Box<Expr<'a>>, // operand
}
// https://pkg.go.dev/go/ast#CallExpr
#[derive(Debug)]
pub struct CallExpr<'a> {
pub fun: Box<Expr<'a>>, // function expression
pub lparen: Position<'a>, // position of "("
pub args: Option<Vec<Expr<'a>>>, // function arguments; or nil
pub ellipsis: Option<Position<'a>>, // position of "..." (token.NoPos if there is no "...")
pub rparen: Position<'a>, // position of ")"
}
// https://pkg.go.dev/go/ast#SelectorExpr
#[derive(Debug)]
pub struct SelectorExpr<'a> {
pub x: Box<Expr<'a>>, // expression
pub sel: Ident<'a>, // field selector
}
// https://pkg.go.dev/go/ast#ParenExpr
#[derive(Debug)]
pub struct ParenExpr<'a> {
pub lparen: Position<'a>, // position of "("
pub x: Box<Expr<'a>>, // parenthesized expression
pub rparen: Position<'a>, // position of ")"
}
// https://pkg.go.dev/go/ast#FuncLit
#[derive(Debug)]
pub struct FuncLit<'a> {
pub type_: FuncType<'a>, // function type
pub body: BlockStmt<'a>, // function body
}
// https://pkg.go.dev/go/ast#ChanType
#[derive(Debug)]
pub struct ChanType<'a> {
pub begin: Position<'a>, // position of "chan" keyword or "<-" (whichever comes first)
pub arrow: Option<Position<'a>>, // position of "<-" (token.NoPos if there is no "<-")
pub dir: u8, // channel direction
pub value: Box<Expr<'a>>, // value type
}
// htt/opt/visual-studio-code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.htmlps://pkg.go.dev/go/ast#IndexExpr
#[derive(Debug)]
pub struct IndexExpr<'a> {
pub x: Box<Expr<'a>>, // expression
pub lbrack: Position<'a>, // position of "["
pub index: Box<Expr<'a>>, // index expression
pub rbrack: Position<'a>, // position of "]"
}
// https://pkg.go.dev/go/ast#MapType
#[derive(Debug)]
pub struct MapType<'a> {
pub map: Position<'a>,
pub key: Box<Expr<'a>>,
pub value: Box<Expr<'a>>,
}
// https://pkg.go.dev/go/ast#CompositeLit
#[derive(Debug)]
pub struct CompositeLit<'a> {
pub type_: Box<Expr<'a>>, // literal type; or nil
pub lbrace: Position<'a>, // position of "{"
pub elts: Option<Vec<Expr<'a>>>, // list of composite elements; or nil
pub rbrace: Position<'a>, // position of "}"
pub incomplete: bool, // true if (source) expressions are missing in the Elts list
}
// https://pkg.go.dev/go/ast#KeyValueExpr
#[derive(Debug)]
pub struct KeyValueExpr<'a> {
pub key: Box<Expr<'a>>,
pub colon: Position<'a>, // position of ":"
pub value: Box<Expr<'a>>,
}
// https://pkg.go.dev/go/ast#ArrayType
#[derive(Debug)]
pub struct ArrayType<'a> {
pub lbrack: Position<'a>, // position of "["
pub len: Option<Box<Expr<'a>>>, // Ellipsis node for [...]T array types, nil for slice types
pub elt: Box<Expr<'a>>, // element type
}
// https://pkg.go.dev/go/ast#ChanDir
#[derive(Debug)]
pub enum ChanDir {
SEND = 1 << 0,
RECV = 1 << 1,
}
// https://pkg.go.dev/go/ast#Spec
#[derive(Debug)]
pub enum Spec {}
// https://pkg.go.dev/go/ast#Expr
#[derive(Debug)]
pub enum Expr<'a> {
ArrayType(ArrayType<'a>),
BasicLit(BasicLit<'a>),
BinaryExpr(BinaryExpr<'a>),
CallExpr(CallExpr<'a>),
ChanType(ChanType<'a>),
CompositeLit(CompositeLit<'a>),
Ellipsis(Ellipsis<'a>),
FuncLit(FuncLit<'a>),
FuncType(FuncType<'a>),
Ident(Ident<'a>),
IndexExpr(IndexExpr<'a>),
InterfaceType(InterfaceType<'a>),
KeyValueExpr(KeyValueExpr<'a>),
MapType(MapType<'a>),
ParenExpr(ParenExpr<'a>),
SelectorExpr(SelectorExpr<'a>),
SliceExpr(SliceExpr<'a>),
StarExpr(StarExpr<'a>),
StructType(StructType<'a>),
TypeAssertExpr(TypeAssertExpr<'a>),
UnaryExpr(UnaryExpr<'a>),
}
// https://pkg.go.dev/go/ast#Stmt
#[derive(Debug)]
pub enum Stmt {}
@@ -1,948 +0,0 @@
// https://golang.org/ref/spec#Lexical_elements
use crate::parser_go_token::{Position, Token};
use phf::{phf_map, Map};
use std::fmt;
use unicode_general_category::{get_general_category, GeneralCategory};
pub type Step<'a> = (Position<'a>, Token, &'a str);
#[derive(Debug)]
pub enum ScannerError {
HexadecimalNotFound,
OctalNotFound,
UnterminatedComment,
UnterminatedEscapedChar,
UnterminatedRune,
UnterminatedString,
InvalidDirective,
}
impl std::error::Error for ScannerError {}
impl fmt::Display for ScannerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "scanner error: {:?}", self)
}
}
pub type Result<T> = std::result::Result<T, ScannerError>;
#[derive(Debug)]
pub struct Scanner<'a> {
directory: &'a str,
file: &'a str,
buffer: &'a str,
//
chars: std::iter::Peekable<std::str::Chars<'a>>,
current_char: Option<char>,
current_char_len: usize,
//
offset: usize,
line: usize,
column: usize,
start_offset: usize,
start_line: usize,
start_column: usize,
//
hide_column: bool,
insert_semi: bool,
pending_line_info: Option<LineInfo<'a>>,
}
type LineInfo<'a> = (Option<&'a str>, usize, Option<usize>, bool);
impl<'a> Scanner<'a> {
pub fn new(filename: &'a str, buffer: &'a str) -> Self {
let (directory, file) = filename.rsplit_once('/').unwrap_or(("", filename));
let mut s = Scanner {
directory,
file,
buffer,
//
chars: buffer.chars().peekable(),
current_char: None,
current_char_len: 0,
//
offset: 0,
line: 1,
column: 1,
start_offset: 0,
start_line: 1,
start_column: 1,
//
hide_column: false,
insert_semi: false,
pending_line_info: None,
};
s.next(); // read the first character
s
}
#[allow(clippy::cognitive_complexity)] // Allow complex scan function
pub fn scan(&mut self) -> Result<Step<'a>> {
let insert_semi = self.insert_semi;
self.insert_semi = false;
while let Some(c) = self.current_char {
self.reset_start();
match c {
' ' | '\t' | '\r' => {
self.next();
}
'\n' => {
self.next();
if insert_semi {
return Ok((self.position(), Token::SEMICOLON, "\n"));
}
}
_ => break,
}
}
if let Some(c) = self.current_char {
match c {
'+' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::ADD_ASSIGN, ""));
}
Some('+') => {
self.insert_semi = true;
self.next();
return Ok((self.position(), Token::INC, ""));
}
_ => return Ok((self.position(), Token::ADD, "")),
}
}
'-' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::SUB_ASSIGN, ""));
}
Some('-') => {
self.insert_semi = true;
self.next();
return Ok((self.position(), Token::DEC, ""));
}
_ => return Ok((self.position(), Token::SUB, "")),
}
}
'*' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::MUL_ASSIGN, ""));
}
_ => return Ok((self.position(), Token::MUL, "")),
}
}
'/' => match self.peek() {
Some('=') => {
self.next();
self.next();
return Ok((self.position(), Token::QUO_ASSIGN, ""));
}
Some('/') => {
if insert_semi {
return Ok((self.position(), Token::SEMICOLON, "\n"));
}
return self.scan_line_comment();
}
Some('*') => {
if insert_semi && self.find_line_end() {
return Ok((self.position(), Token::SEMICOLON, "\n"));
}
return self.scan_general_comment();
}
_ => {
self.next();
return Ok((self.position(), Token::QUO, ""));
}
},
'%' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::REM_ASSIGN, ""));
}
_ => return Ok((self.position(), Token::REM, "")),
}
}
'&' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::AND_ASSIGN, ""));
}
Some('&') => {
self.next();
return Ok((self.position(), Token::LAND, ""));
}
Some('^') => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::AND_NOT_ASSIGN, ""));
}
_ => return Ok((self.position(), Token::AND_NOT, "")),
}
}
_ => return Ok((self.position(), Token::AND, "")),
}
}
'|' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::OR_ASSIGN, ""));
}
Some('|') => {
self.next();
return Ok((self.position(), Token::LOR, ""));
}
_ => return Ok((self.position(), Token::OR, "")),
}
}
'^' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::XOR_ASSIGN, ""));
}
_ => return Ok((self.position(), Token::XOR, "")),
}
}
'<' => {
self.next();
match self.current_char {
Some('<') => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::SHL_ASSIGN, ""));
}
_ => return Ok((self.position(), Token::SHL, "")),
}
}
Some('=') => {
self.next();
return Ok((self.position(), Token::LEQ, ""));
}
Some('-') => {
self.next();
return Ok((self.position(), Token::ARROW, ""));
}
_ => return Ok((self.position(), Token::LSS, "")),
}
}
'>' => {
self.next();
match self.current_char {
Some('>') => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::SHR_ASSIGN, ""));
}
_ => {
return Ok((self.position(), Token::SHR, ""));
}
}
}
Some('=') => {
self.next();
return Ok((self.position(), Token::GEQ, ""));
}
_ => return Ok((self.position(), Token::GTR, "")),
}
}
':' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::DEFINE, ""));
}
_ => return Ok((self.position(), Token::COLON, "")),
}
}
'!' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::NEQ, ""));
}
_ => return Ok((self.position(), Token::NOT, "")),
}
}
',' => {
self.next();
return Ok((self.position(), Token::COMMA, ""));
}
'(' => {
self.next();
return Ok((self.position(), Token::LPAREN, ""));
}
')' => {
self.insert_semi = true;
self.next();
return Ok((self.position(), Token::RPAREN, ""));
}
'[' => {
self.next();
return Ok((self.position(), Token::LBRACK, ""));
}
']' => {
self.insert_semi = true;
self.next();
return Ok((self.position(), Token::RBRACK, ""));
}
'{' => {
self.next();
return Ok((self.position(), Token::LBRACE, ""));
}
'}' => {
self.insert_semi = true;
self.next();
return Ok((self.position(), Token::RBRACE, ""));
}
';' => {
self.next();
return Ok((self.position(), Token::SEMICOLON, ";"));
}
'.' => {
self.next();
match self.current_char {
Some('0'..='9') => return self.scan_int_or_float_or_imag(true),
Some('.') => match self.peek() {
Some('.') => {
self.next();
self.next();
return Ok((self.position(), Token::ELLIPSIS, ""));
}
_ => return Ok((self.position(), Token::PERIOD, "")),
},
_ => return Ok((self.position(), Token::PERIOD, "")),
}
}
'=' => {
self.next();
match self.current_char {
Some('=') => {
self.next();
return Ok((self.position(), Token::EQL, ""));
}
_ => return Ok((self.position(), Token::ASSIGN, "")),
}
}
'0'..='9' => return self.scan_int_or_float_or_imag(false),
'\'' => return self.scan_rune(),
'"' => return self.scan_interpreted_string(),
'`' => return self.scan_raw_string(),
_ => return self.scan_pkg_or_keyword_or_ident(),
};
}
self.reset_start();
if insert_semi {
Ok((self.position(), Token::SEMICOLON, "\n"))
} else {
Ok((self.position(), Token::EOF, ""))
}
}
// https://golang.org/ref/spec#Keywords
// https://golang.org/ref/spec#Identifiers
fn scan_pkg_or_keyword_or_ident(&mut self) -> Result<Step<'a>> {
self.next();
while let Some(c) = self.current_char {
if !(is_letter(c) || is_unicode_digit(c)) {
break;
}
self.next()
}
let pos = self.position();
let literal = self.literal();
if literal.len() > 1 {
if let Some(&token) = KEYWORDS.get(literal) {
self.insert_semi = matches!(
token,
Token::BREAK | Token::CONTINUE | Token::FALLTHROUGH | Token::RETURN
);
return Ok((pos, token, literal));
}
}
self.insert_semi = true;
Ok((pos, Token::IDENT, literal))
}
// https://golang.org/ref/spec#Integer_literals
// https://golang.org/ref/spec#Floating-point_literals
// https://golang.org/ref/spec#Imaginary_literals
fn scan_int_or_float_or_imag(&mut self, preceding_dot: bool) -> Result<Step<'a>> {
self.insert_semi = true;
let mut token = Token::INT;
let mut digits = "_0123456789";
let mut exp = "eE";
if !preceding_dot {
if matches!(self.current_char, Some('0')) {
self.next();
match self.current_char {
Some('b' | 'B') => {
digits = "_01";
exp = "";
self.next();
}
Some('o' | 'O') => {
digits = "_01234567";
exp = "";
self.next();
}
Some('x' | 'X') => {
digits = "_0123456789abcdefABCDEF";
exp = "pP";
self.next();
}
_ => {}
};
}
while let Some(c) = self.current_char {
if !digits.contains(c) {
break;
}
self.next();
}
}
if preceding_dot || matches!(self.current_char, Some('.')) {
token = Token::FLOAT;
self.next();
while let Some(c) = self.current_char {
if !digits.contains(c) {
break;
}
self.next();
}
}
if !exp.is_empty() {
if let Some(c) = self.current_char {
if exp.contains(c) {
token = Token::FLOAT;
self.next();
if matches!(self.current_char, Some('-' | '+')) {
self.next();
}
while let Some(c) = self.current_char {
if !matches!(c, '_' | '0'..='9') {
break;
}
self.next();
}
}
}
}
if matches!(self.current_char, Some('i')) {
token = Token::IMAG;
self.next();
}
Ok((self.position(), token, self.literal()))
}
// https://golang.org/ref/spec#Rune_literals
fn scan_rune(&mut self) -> Result<Step<'a>> {
self.insert_semi = true;
self.next();
match self.current_char {
Some('\\') => self.require_escaped_char::<'\''>()?,
Some(_) => self.next(),
_ => return Err(ScannerError::UnterminatedRune),
}
if matches!(self.current_char, Some('\'')) {
self.next();
return Ok((self.position(), Token::CHAR, self.literal()));
}
Err(ScannerError::UnterminatedRune)
}
// https://golang.org/ref/spec#String_literals
fn scan_interpreted_string(&mut self) -> Result<Step<'a>> {
self.insert_semi = true;
self.next();
while let Some(c) = self.current_char {
match c {
'"' => {
self.next();
return Ok((self.position(), Token::STRING, self.literal()));
}
'\\' => self.require_escaped_char::<'"'>()?,
_ => self.next(),
}
}
Err(ScannerError::UnterminatedString)
}
// https://golang.org/ref/spec#String_literals
fn scan_raw_string(&mut self) -> Result<Step<'a>> {
self.insert_semi = true;
self.next();
while let Some(c) = self.current_char {
match c {
'`' => {
self.next();
return Ok((self.position(), Token::STRING, self.literal()));
}
_ => self.next(),
}
}
Err(ScannerError::UnterminatedString)
}
// https://golang.org/ref/spec#Comments
fn scan_general_comment(&mut self) -> Result<Step<'a>> {
self.next();
self.next();
while let Some(c) = self.current_char {
match c {
'*' => {
self.next();
if matches!(self.current_char, Some('/')) {
self.next();
let pos = self.position();
let lit = self.literal();
// look for compiler directives
self.directive(&lit["/*".len()..lit.len() - "*/".len()], true)?;
return Ok((pos, Token::COMMENT, lit));
}
}
_ => self.next(),
}
}
Err(ScannerError::UnterminatedComment)
}
// https://golang.org/ref/spec#Comments
fn scan_line_comment(&mut self) -> Result<Step<'a>> {
self.next();
self.next();
while let Some(c) = self.current_char {
if is_newline(c) {
break;
}
self.next();
}
let pos = self.position();
let lit = self.literal();
// look for compiler directives (at the beginning of line)
if self.start_column == 1 {
self.directive(lit["//".len()..].trim_end(), false)?;
}
Ok((pos, Token::COMMENT, self.literal()))
}
// https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives
fn directive(&mut self, input: &'a str, immediate: bool) -> Result<()> {
if let Some(line_directive) = input.strip_prefix("line ") {
self.pending_line_info = self.parse_line_directive(line_directive)?;
if immediate {
self.consume_pending_line_info();
}
}
Ok(())
}
fn parse_line_directive(&mut self, line_directive: &'a str) -> Result<Option<LineInfo<'a>>> {
if let Some((file, line)) = line_directive.rsplit_once(':') {
let line = line.parse().map_err(|_| ScannerError::InvalidDirective)?;
if let Some((file, l)) = file.rsplit_once(':') {
if let Ok(l) = l.parse() {
//line :line:col
//line filename:line:col
/*line :line:col*/
/*line filename:line:col*/
let file = if !file.is_empty() { Some(file) } else { None };
let col = Some(line);
let line = l;
let hide_column = false;
return Ok(Some((file, line, col, hide_column)));
}
}
//line :line
//line filename:line
/*line :line*/
/*line filename:line*/
Ok(Some((Some(file), line, None, true)))
} else {
Ok(None)
}
}
const fn find_line_end(&self) -> bool {
let buffer = self.buffer.as_bytes();
let mut in_comment = true;
let mut i = self.offset;
let max = self.buffer.len();
while i < max {
let c = buffer[i] as char;
if i < max - 1 {
let n = buffer[i + 1] as char;
if !in_comment && c == '/' && n == '/' {
return true;
}
if c == '/' && n == '*' {
i += 2;
in_comment = true;
continue;
}
if c == '*' && n == '/' {
i += 2;
in_comment = false;
continue;
}
}
if is_newline(c) {
return true;
}
if !in_comment && !matches!(c, ' ' | '\t' | '\r') {
return false;
}
i += 1;
}
!in_comment
}
fn consume_pending_line_info(&mut self) {
if let Some(line_info) = self.pending_line_info.take() {
if let Some(file) = line_info.0 {
self.file = file;
}
self.line = line_info.1;
if let Some(column) = line_info.2 {
self.column = column;
}
self.hide_column = line_info.3;
}
}
fn peek(&mut self) -> Option<char> {
self.chars.peek().copied()
}
fn next(&mut self) {
self.offset += self.current_char_len;
self.column += self.current_char_len;
let last_char = self.current_char;
self.current_char = self.chars.next();
if let Some(c) = self.current_char {
self.current_char_len = c.len_utf8();
if matches!(last_char, Some('\n')) {
self.line += 1;
self.column = 1;
self.consume_pending_line_info();
}
} else {
self.current_char_len = 0
}
}
const fn position(&self) -> Position<'a> {
Position {
directory: self.directory,
file: self.file,
offset: self.start_offset,
line: self.start_line,
column: if self.hide_column {
0
} else {
self.start_column
},
}
}
fn reset_start(&mut self) {
self.start_offset = self.offset;
self.start_line = self.line;
self.start_column = self.column;
}
fn literal(&self) -> &'a str {
&self.buffer[self.start_offset..self.offset]
}
fn require_escaped_char<const DELIM: char>(&mut self) -> Result<()> {
self.next();
let c = self
.current_char
.ok_or(ScannerError::UnterminatedEscapedChar)?;
// TODO: move this to the match when const generics can be referenced in patterns
if c == DELIM {
self.next();
return Ok(());
}
match c {
'a' | 'b' | 'f' | 'n' | 'r' | 't' | 'v' | '\\' => self.next(),
'x' => {
self.next();
self.require_hex_digits::<2>()?
}
'u' => {
self.next();
self.require_hex_digits::<4>()?;
}
'U' => {
self.next();
self.require_hex_digits::<8>()?;
}
'0'..='7' => self.require_octal_digits::<3>()?,
_ => return Err(ScannerError::UnterminatedEscapedChar),
}
Ok(())
}
fn require_octal_digits<const COUNT: usize>(&mut self) -> Result<()> {
for _ in 0..COUNT {
let c = self.current_char.ok_or(ScannerError::OctalNotFound)?;
if !is_octal_digit(c) {
return Err(ScannerError::OctalNotFound);
}
self.next();
}
Ok(())
}
fn require_hex_digits<const COUNT: usize>(&mut self) -> Result<()> {
for _ in 0..COUNT {
let c = self.current_char.ok_or(ScannerError::HexadecimalNotFound)?;
if !is_hex_digit(c) {
return Err(ScannerError::HexadecimalNotFound);
}
self.next();
}
Ok(())
}
}
impl<'a> IntoIterator for Scanner<'a> {
type Item = Result<Step<'a>>;
type IntoIter = IntoIter<'a>;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter::new(self)
}
}
pub struct IntoIter<'a> {
scanner: Scanner<'a>,
done: bool,
}
impl<'a> IntoIter<'a> {
const fn new(scanner: Scanner<'a>) -> Self {
Self { scanner, done: false }
}
}
impl<'a> Iterator for IntoIter<'a> {
type Item = Result<Step<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
match self.scanner.scan() {
Ok((pos, tok, lit)) => {
if tok == Token::EOF {
self.done = true;
}
Some(Ok((pos, tok, lit)))
}
Err(err) => {
self.done = true;
Some(Err(err))
}
}
}
}
// https://golang.org/ref/spec#Letters_and_digits
fn is_letter(c: char) -> bool {
c == '_' || is_unicode_letter(c)
}
//const fn is_decimal_digit(c: char) -> bool {
//matches!(c, '0'..='9')
//}
//const fn is_binary_digit(c: char) -> bool {
//matches!(c, '0'..='1')
//}
const fn is_octal_digit(c: char) -> bool {
matches!(c, '0'..='7')
}
const fn is_hex_digit(c: char) -> bool {
matches!(c, '0'..='9' | 'A'..='F' | 'a'..='f')
}
// https://golang.org/ref/spec#Characters
const fn is_newline(c: char) -> bool {
c == '\n'
}
//const fn is_unicode_char(c: char) -> bool {
//c != '\n'
//}
fn is_unicode_letter(c: char) -> bool {
matches!(
get_general_category(c),
GeneralCategory::UppercaseLetter
| GeneralCategory::LowercaseLetter
| GeneralCategory::TitlecaseLetter
| GeneralCategory::ModifierLetter
| GeneralCategory::OtherLetter
)
}
fn is_unicode_digit(c: char) -> bool {
get_general_category(c) == GeneralCategory::DecimalNumber
}
// https://golang.org/ref/spec#Keywords
static KEYWORDS: Map<&'static str, Token> = phf_map! {
"break" => Token::BREAK,
"case" => Token::CASE,
"chan" => Token::CHAN,
"const" => Token::CONST,
"continue" => Token::CONTINUE,
"default" => Token::DEFAULT,
"defer" => Token::DEFER,
"else" => Token::ELSE,
"fallthrough" => Token::FALLTHROUGH,
"for" => Token::FOR,
"func" => Token::FUNC,
"go" => Token::GO,
"goto" => Token::GOTO,
"if" => Token::IF,
"import" => Token::IMPORT,
"interface" => Token::INTERFACE,
"map" => Token::MAP,
"package" => Token::PACKAGE,
"range" => Token::RANGE,
"return" => Token::RETURN,
"select" => Token::SELECT,
"struct" => Token::STRUCT,
"switch" => Token::SWITCH,
"type" => Token::TYPE,
"var" => Token::VAR,
};
#[cfg(test)]
mod tests {
use super::Scanner;
#[test] // fuzz
fn it_should_return_an_error_on_missing_line_number() {
let input = "/*line :*/";
let mut out: Vec<_> = Scanner::new(file!(), input).into_iter().collect();
assert!(out.pop().unwrap().is_err());
}
}
@@ -1,273 +0,0 @@
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/go/token/token.go
#![allow(non_camel_case_types)] // For consistency with the Go tokens
use std::fmt;
#[derive(Clone, Copy, Debug, Default)]
pub struct Position<'a> {
pub directory: &'a str,
pub file: &'a str,
pub offset: usize,
pub line: usize,
pub column: usize,
}
impl<'a> fmt::Display for Position<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.file.is_empty() {
write!(f, ":{}:{}", self.line, self.column)
} else if self.file.starts_with('/') {
write!(f, "{}:{}:{}", self.file, self.line, self.column)
} else {
write!(
f,
"{}/{}:{}:{}",
self.directory, self.file, self.line, self.column
)
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Token {
EOF,
COMMENT,
IDENT, // main
INT, // 12345
FLOAT, // 123.45
IMAG, // 123.45i
CHAR, // 'a'
STRING, // "abc"
ADD, // +
SUB, // -
MUL, // *
QUO, // /
REM, // %
AND, // &
OR, // |
XOR, // ^
SHL, // <<
SHR, // >>
AND_NOT, // &^
ADD_ASSIGN, // +=
SUB_ASSIGN, // -=
MUL_ASSIGN, // *=
QUO_ASSIGN, // /=
REM_ASSIGN, // %=
AND_ASSIGN, // &=
OR_ASSIGN, // |=
XOR_ASSIGN, // ^=
SHL_ASSIGN, // <<=
SHR_ASSIGN, // >>=
AND_NOT_ASSIGN, // &^=
LAND, // &&
LOR, // ||
ARROW, // <-
INC, // ++
DEC, // --
EQL, // ==
LSS, // <
GTR, // >
ASSIGN, // =
NOT, // !
NEQ, // !=
LEQ, // <=
GEQ, // >=
DEFINE, // :=
ELLIPSIS, // ...
LPAREN, // (
LBRACK, // [
LBRACE, // {
COMMA, // ,
PERIOD, // .
RPAREN, // )
RBRACK, // ]
RBRACE, // }
SEMICOLON, // ;
COLON, // :
BREAK,
CASE,
CHAN,
CONST,
CONTINUE,
DEFAULT,
DEFER,
ELSE,
FALLTHROUGH,
FOR,
FUNC,
GO,
GOTO,
IF,
IMPORT,
INTERFACE,
MAP,
PACKAGE,
RANGE,
RETURN,
SELECT,
STRUCT,
SWITCH,
TYPE,
VAR,
}
impl Token {
pub const fn is_assign_op(&self) -> bool {
use Token::*;
matches!(
self,
ADD_ASSIGN
| SUB_ASSIGN
| MUL_ASSIGN
| QUO_ASSIGN
| REM_ASSIGN
| AND_ASSIGN
| OR_ASSIGN
| XOR_ASSIGN
| SHL_ASSIGN
| SHR_ASSIGN
| AND_NOT_ASSIGN
)
}
// https://go.dev/ref/spec#Operator_precedence
pub fn precedence(&self) -> u8 {
use Token::*;
match self {
MUL | QUO | REM | SHL | SHR | AND | AND_NOT => 5,
ADD | SUB | OR | XOR => 4,
EQL | NEQ | LSS | LEQ | GTR | GEQ => 3,
LAND => 2,
LOR => 1,
_ => unreachable!(
"precedence() is only supported for binary operators, called with: {:?}",
self
),
}
}
pub const fn lowest_precedence() -> u8 {
0
}
}
impl From<&Token> for &'static str {
fn from(token: &Token) -> Self {
use Token::*;
match token {
EOF => "EOF",
COMMENT => "COMMENT",
IDENT => "IDENT",
INT => "INT",
FLOAT => "FLOAT",
IMAG => "IMAG",
CHAR => "CHAR",
STRING => "STRING",
ADD => "+",
SUB => "-",
MUL => "*",
QUO => "/",
REM => "%",
AND => "&",
OR => "|",
XOR => "^",
SHL => "<<",
SHR => ">>",
AND_NOT => "&^",
ADD_ASSIGN => "+=",
SUB_ASSIGN => "-=",
MUL_ASSIGN => "*=",
QUO_ASSIGN => "/=",
REM_ASSIGN => "%=",
AND_ASSIGN => "&=",
OR_ASSIGN => "|=",
XOR_ASSIGN => "^=",
SHL_ASSIGN => "<<=",
SHR_ASSIGN => ">>=",
AND_NOT_ASSIGN => "&^=",
LAND => "&&",
LOR => "||",
ARROW => "<-",
INC => "++",
DEC => "--",
EQL => "==",
LSS => "<",
GTR => ">",
ASSIGN => "=",
NOT => "!",
NEQ => "!=",
LEQ => "<=",
GEQ => ">=",
DEFINE => ":=",
ELLIPSIS => "...",
LPAREN => "(",
LBRACK => "[",
LBRACE => "{",
COMMA => ",",
PERIOD => ".",
RPAREN => ")",
RBRACK => "]",
RBRACE => "}",
SEMICOLON => ";",
COLON => ":",
BREAK => "break",
CASE => "case",
CHAN => "chan",
CONST => "const",
CONTINUE => "continue",
DEFAULT => "default",
DEFER => "defer",
ELSE => "else",
FALLTHROUGH => "fallthrough",
FOR => "for",
FUNC => "func",
GO => "go",
GOTO => "goto",
IF => "if",
IMPORT => "import",
INTERFACE => "interface",
MAP => "map",
PACKAGE => "package",
RANGE => "range",
RETURN => "return",
SELECT => "select",
STRUCT => "struct",
SWITCH => "switch",
TYPE => "type",
VAR => "var",
}
}
}
@@ -218,7 +218,7 @@ pub fn parse_python_imports(code: &str) -> error::Result<Vec<String>> {
let ast = parser::parse_program(code, "main.py").map_err(|e| {
error::Error::ExecutionErr(format!("Error parsing code: {}", e.to_string()))
})?;
let imports = ast
let mut imports: Vec<String> = ast
.into_iter()
.filter_map(|x| match x {
Located { node, .. } => match node {
@@ -250,7 +250,7 @@ pub fn parse_python_imports(code: &str) -> error::Result<Vec<String>> {
.filter(|x| !STDIMPORTS.contains(&x.as_str()))
.unique()
.collect();
imports.sort();
Ok(imports)
}
}
@@ -460,7 +460,7 @@ def main():
";
let r = parse_python_imports(code)?;
// println!("{}", serde_json::to_string(&r)?);
assert_eq!(r, vec!["wmill", "zanzibar", "matplotlib", "requests"]);
assert_eq!(r, vec!["matplotlib", "requests", "wmill", "zanzibar"]);
Ok(())
}
+13 -13
View File
@@ -1808,6 +1808,19 @@
},
"query": "\n UPDATE queue\n SET flow_status = JSONB_SET(flow_status, ARRAY['step'], $1)\n WHERE id = $2\n "
},
"4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
}
},
"query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2"
},
"5061c0d054bf4f028e7fe51a8f9389024c6ae4492755cadac0f7167e5300bda0": {
"describe": {
"columns": [],
@@ -3874,19 +3887,6 @@
},
"query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app.id = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]"
},
"9baf1f88ab6c1df642cf8f0f49805ccac3e48cce1f0e184fb9db78536a8ca7b0": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Left": [
"Varchar",
"Text"
]
}
},
"query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() - ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2"
},
"9db64c9ff790d8c833c1e831a87803c32103ce9de68cc9c08d6f56cc988d7e37": {
"describe": {
"columns": [
+75 -23
View File
@@ -14,6 +14,7 @@ use once_cell::sync::OnceCell;
use regex::Regex;
use sqlx::{Pool, Postgres, Transaction};
use windmill_api_client::Client;
use windmill_parser_go::parse_go_imports;
use std::{
borrow::Borrow, collections::HashMap, io, os::unix::process::ExitStatusExt, panic,
process::Stdio, time::Duration, sync::atomic::Ordering,
@@ -340,7 +341,7 @@ const NSJAIL_CONFIG_RUN_BASH_CONTENT: &str = include_str!("../nsjail/run.bash.co
const RELATIVE_PYTHON_LOADER: &str = include_str!("../loader.py");
const GO_REQ_SPLITTER: &str = "//go.sum";
const GO_REQ_SPLITTER: &str = "//go.sum\n";
#[derive(Clone)]
pub struct Metrics {
@@ -1319,6 +1320,20 @@ mount {{
result
}
async fn gen_go_mod(inner_content: &str, job_dir: &str, requirements: &str) -> error::Result<(bool, bool)> {
gen_go_mymod(inner_content, job_dir).await?;
let md = requirements
.split_once(GO_REQ_SPLITTER);
if let Some((req, sum)) = md {
write_file(job_dir, "go.mod", &req).await?;
write_file(job_dir, "go.sum", &sum).await?;
Ok((true, true))
} else {
write_file(job_dir, "go.mod", &requirements).await?;
Ok((true, false))
}
}
#[tracing::instrument(level = "trace", skip_all)]
async fn handle_go_job(
logs: &mut String,
@@ -1335,19 +1350,10 @@ async fn handle_go_job(
) -> Result<serde_json::Value, Error> {
//go does not like executing modules at temp root
let job_dir = &format!("{job_dir}/go");
let skip_go_mod = if let Some(requirements) = requirements_o {
gen_go_mymod(inner_content, job_dir).await?;
// TODO: remove after some time in favor of just requirements
// this is just migration code from a time we also stored go.sum
let md = requirements
.split_once(GO_REQ_SPLITTER)
.map(|x| x.0)
.unwrap_or(&requirements);
write_file(job_dir, "go.mod", &md).await?;
true
let (skip_go_mod, skip_tidy) = if let Some(requirements) = requirements_o {
gen_go_mod(inner_content, job_dir, &requirements).await?
} else {
false
(false, false)
};
logs.push_str("\n\n--- GO DEPENDENCIES SETUP ---\n");
set_logs(logs, &job.id, db).await;
@@ -1360,6 +1366,7 @@ async fn handle_go_job(
db,
true,
skip_go_mod,
skip_tidy,
worker_name,
&job.workspace_id,
)
@@ -2256,6 +2263,7 @@ async fn capture_dependency_job(
db,
false,
false,
false,
worker_name,
w_id
)
@@ -2331,7 +2339,7 @@ async fn pip_compile(
.collect::<Vec<String>>()
.join("\n");
sqlx::query!(
"INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() - ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
"INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
req_hash,
lockfile
).fetch_optional(db).await?;
@@ -2344,8 +2352,9 @@ async fn install_go_dependencies(
logs: &mut String,
job_dir: &str,
db: &sqlx::Pool<sqlx::Postgres>,
preview: bool,
non_dep_job: bool,
skip_go_mod: bool,
has_sum: bool,
worker_name: &str,
w_id: &str
) -> error::Result<String> {
@@ -2360,10 +2369,39 @@ async fn install_go_dependencies(
handle_child(job_id, db, logs, child, false, worker_name, w_id).await?;
}
let mut new_lockfile = false;
let hash = if !has_sum {
calculate_hash(parse_go_imports(&code)?.iter().join("\n").as_str())
} else {
"".to_string()
};
let mut skip_tidy = has_sum;
if !has_sum {
if let Some(cached) = sqlx::query_scalar!(
"SELECT lockfile FROM pip_resolution_cache WHERE hash = $1",
hash
).fetch_optional(db).await? {
logs.push_str(&format!("\nfound cached resolution"));
gen_go_mod(code, job_dir, &cached).await?;
skip_tidy = true;
new_lockfile = false;
} else {
new_lockfile = true;
}
}
let mod_command = if skip_tidy {
"download"
} else {
"tidy"
};
let child = Command::new(GO_PATH.as_str())
.current_dir(job_dir)
.env("GOPATH", GO_CACHE_DIR)
.args(vec!["mod", "tidy"])
.args(vec!["mod", mod_command])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
@@ -2371,14 +2409,28 @@ async fn install_go_dependencies(
.await
.map_err(|e| Error::ExecutionErr(format!("Lock file generation failed: {e:?}")))?;
if preview {
Ok(String::new())
if (!new_lockfile || has_sum) && non_dep_job{
return Ok("".to_string());
}
let mut req_content = "".to_string();
let mut file = File::open(format!("{job_dir}/go.mod")).await?;
file.read_to_string(&mut req_content).await?;
req_content.push_str(GO_REQ_SPLITTER);
let mut file = File::open(format!("{job_dir}/go.sum")).await?;
file.read_to_string(&mut req_content).await?;
if non_dep_job {
sqlx::query!(
"INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
hash,
req_content
).fetch_optional(db).await?;
return Ok(String::new())
} else {
let mut req_content = "".to_string();
let mut file = File::open(format!("{job_dir}/go.mod")).await?;
file.read_to_string(&mut req_content).await?;
Ok(req_content)
}
}