mirror of
https://github.com/ManyakRus/image_database.git
synced 2024-11-21 05:05:49 +02:00
новый
This commit is contained in:
parent
5c544aea05
commit
fdb7f76b8e
3
.env_example
Normal file
3
.env_example
Normal file
@ -0,0 +1,3 @@
|
||||
DIRECTORY_SOURCE=./
|
||||
FILENAME_GRAPHML=./connections.graphml
|
||||
SERVICE_NAME=Main
|
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/.env
|
||||
/cover.out
|
||||
/internal/v0/app/.env
|
||||
.env
|
||||
|
||||
/.idea/
|
||||
/bin/.env
|
||||
/log.txt
|
||||
|
||||
.vscode
|
||||
/bin/image_connections
|
||||
/settings/connections_add.txt
|
||||
/bin/settings/connections_add.txt
|
52
Makefile
Normal file
52
Makefile
Normal file
@ -0,0 +1,52 @@
|
||||
SERVICENAME=image_database
|
||||
SERVICEURL=github.com/ManyakRus/$(SERVICENAME)
|
||||
|
||||
FILEMAIN=./internal/main.go
|
||||
FILEAPP=./bin/$(SERVICENAME)
|
||||
|
||||
NEW_REPO=github.com/ManyakRus/image_database
|
||||
|
||||
|
||||
run:
|
||||
clear
|
||||
go build -race -o $(FILEAPP) $(FILEMAIN)
|
||||
# cd ./bin && \
|
||||
./bin/app_race
|
||||
mod:
|
||||
clear
|
||||
go get -u ./...
|
||||
go mod tidy -compat=1.18
|
||||
go mod vendor
|
||||
go fmt ./...
|
||||
build:
|
||||
clear
|
||||
go build -race -o $(FILEAPP) $(FILEMAIN)
|
||||
cd ./cmd && \
|
||||
./VersionToFile.py
|
||||
cp $(FILEAPP) $(GOPATH)/bin
|
||||
|
||||
lint:
|
||||
clear
|
||||
go fmt ./...
|
||||
golangci-lint run ./internal/v0/...
|
||||
golangci-lint run ./pkg/v0/...
|
||||
gocyclo -over 10 ./internal/v0
|
||||
gocyclo -over 10 ./pkg/v0
|
||||
gocritic check ./internal/v0/...
|
||||
gocritic check ./pkg/v0/...
|
||||
staticcheck ./internal/v0/...
|
||||
staticcheck ./pkg/v0/...
|
||||
run.test:
|
||||
clear
|
||||
go fmt ./...
|
||||
go test -coverprofile cover.out ./internal/v0/app/...
|
||||
go tool cover -func=cover.out
|
||||
newrepo:
|
||||
sed -i 's+$(SERVICEURL)+$(NEW_REPO)+g' go.mod
|
||||
find -name *.go -not -path "*/vendor/*"|xargs sed -i 's+$(SERVICEURL)+$(NEW_REPO)+g'
|
||||
graph:
|
||||
clear
|
||||
image_packages ./ docs/packages.graphml
|
||||
conn:
|
||||
clear
|
||||
image_connections ./internal docs/connections.graphml $(SERVICENAME)
|
2
bin/.gitignore
vendored
Normal file
2
bin/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/app_race
|
||||
/log.txt
|
1
bin/date.txt
Normal file
1
bin/date.txt
Normal file
@ -0,0 +1 @@
|
||||
2023-08-30 07:32:03.868
|
32
bin/settings/connections.txt
Normal file
32
bin/settings/connections.txt
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"github.com/segmentio/kafka-go": "Kafka",
|
||||
"gorm.io/driver/postgres": "Postgres",
|
||||
"github.com/camunda/zeebe/clients/go/v8": "Camunda",
|
||||
"gitlab.aescorp.ru/dsp_dev/claim/nikitin/nats_connect": "Nats",
|
||||
"github.com/nats-io/nats.go": "Nats",
|
||||
"github.com/minio/minio-go": "Minio",
|
||||
"github.com/sashabaranov/go-openai": "Chat GPT",
|
||||
"github.com/xhit/go-simple-mail": "EMail server",
|
||||
"github.com/emersion/go-imap": "EMail IMAP server",
|
||||
"github.com/denisenkom/go-mssqldb": "MSSQL",
|
||||
"github.com/lib/pq": "Postgres",
|
||||
"github.com/jackc/pgx": "Postgres",
|
||||
"github.com/gotd": "Telegram",
|
||||
"go.mau.fi/whatsmeow": "Whatsapp",
|
||||
"github.com/mattn/go-sqlite3": "SQL Lite3",
|
||||
"net/http": "WEB",
|
||||
"github.com/valyala/fasthttp": "WEB",
|
||||
"github.com/gin-gonic/gin": "WEB",
|
||||
"github.com/rabbitmq/amqp091-go": "RabbitMQ",
|
||||
"github.com/ManyakRus/starter/kafka_connect": "Kafka",
|
||||
"github.com/ManyakRus/starter/postgres_gorm": "Postgres",
|
||||
"github.com/ManyakRus/starter/camunda_connect": "Camunda",
|
||||
"github.com/ManyakRus/starter/nats_connect": "Nats",
|
||||
"github.com/ManyakRus/starter/minio_connect": "Minio",
|
||||
"github.com/ManyakRus/starter/liveness": "WEB server\nLiveness",
|
||||
"github.com/ManyakRus/starter/whatsapp_connect": "Whatsapp",
|
||||
"github.com/go-redis/redis": "Redis",
|
||||
"github.com/gorilla/websocket": "Web socket",
|
||||
"github.com/Nerzal/gocloak": "Keycloak",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp": "Prometeus"
|
||||
}
|
6
bin/start_with_log.sh
Executable file
6
bin/start_with_log.sh
Executable file
@ -0,0 +1,6 @@
|
||||
logfile=log.txt
|
||||
|
||||
echo press CTRL+C to stop app
|
||||
echo log file: $logfile
|
||||
|
||||
script -q /dev/null -c ./go-xgml > $logfile
|
1
bin/subversion.txt
Normal file
1
bin/subversion.txt
Normal file
@ -0,0 +1 @@
|
||||
00129
|
1
bin/version.txt
Normal file
1
bin/version.txt
Normal file
@ -0,0 +1 @@
|
||||
v0
|
52
cmd/VersionToFile.py
Executable file
52
cmd/VersionToFile.py
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python3
|
||||
# Python script to create an empty file
|
||||
# with current date as name.
|
||||
|
||||
# importing datetime module
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
# datetime.datetime.now() to get
|
||||
# current date as filename.
|
||||
# TimeNow = datetime.datetime.now()
|
||||
|
||||
FILESUBVERSION="../bin/subversion.txt"
|
||||
FILEDATE="../bin/date.txt"
|
||||
|
||||
# create empty file
|
||||
def create_file():
|
||||
fmt = "%Y-%m-%d %H:%M:%S.%f"
|
||||
str1 = datetime.utcnow().strftime(fmt)[:-3]
|
||||
|
||||
# Function creates an empty file
|
||||
# %d - date, %B - month, %Y - Year
|
||||
with open(FILEDATE, "w") as file:
|
||||
file.write(str1)
|
||||
file.close()
|
||||
|
||||
def set_version():
|
||||
filename=FILESUBVERSION
|
||||
build=0
|
||||
mode = 'r' if os.path.exists(filename) else 'w+'
|
||||
with open(filename, encoding="utf8", mode=mode) as file_in:
|
||||
_str_build = file_in.read()
|
||||
file_in.close()
|
||||
try:
|
||||
build = int(_str_build)
|
||||
except ValueError as err:
|
||||
print("Build.__setVers(): при конвертировании строки в число, err=", err)
|
||||
finally:
|
||||
pass
|
||||
build += 1
|
||||
str_build = str(build)
|
||||
while len(str_build) < 5:
|
||||
str_build = "0" + str_build
|
||||
print("Build.set_version(): new build=", str_build)
|
||||
with open(filename, "w", encoding="utf8") as file_in:
|
||||
file_in.write(str_build)
|
||||
file_in.close()
|
||||
|
||||
|
||||
# Driver Code
|
||||
create_file()
|
||||
set_version()
|
52
cmd/test_copy/VersionToFile.py
Executable file
52
cmd/test_copy/VersionToFile.py
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/python3
|
||||
# Python script to create an empty file
|
||||
# with current date as name.
|
||||
|
||||
# importing datetime module
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
||||
# datetime.datetime.now() to get
|
||||
# current date as filename.
|
||||
# TimeNow = datetime.datetime.now()
|
||||
|
||||
FILESUBVERSION="subversion.txt"
|
||||
FILEDATE="date.txt"
|
||||
|
||||
# create empty file
|
||||
def create_file():
|
||||
fmt = "%Y-%m-%d %H:%M:%S.%f"
|
||||
str1 = datetime.utcnow().strftime(fmt)[:-3]
|
||||
|
||||
# Function creates an empty file
|
||||
# %d - date, %B - month, %Y - Year
|
||||
with open(FILEDATE, "w") as file:
|
||||
file.write(str1)
|
||||
file.close()
|
||||
|
||||
def set_vers():
|
||||
filename=FILESUBVERSION
|
||||
build=0
|
||||
mode = 'r' if os.path.exists(filename) else 'w+'
|
||||
with open(filename, encoding="utf8", mode=mode) as file_in:
|
||||
_str_build = file_in.read()
|
||||
file_in.close()
|
||||
try:
|
||||
build = int(_str_build)
|
||||
except ValueError as err:
|
||||
print("Build.__setVers(): при конвертировании строки в число, err=", err)
|
||||
finally:
|
||||
pass
|
||||
build += 1
|
||||
str_build = str(build)
|
||||
while len(str_build) < 5:
|
||||
str_build = "0" + str_build
|
||||
print("Build.__set_vers(): new build=", str_build)
|
||||
with open(filename, "w", encoding="utf8") as file_in:
|
||||
file_in.write(str_build)
|
||||
file_in.close()
|
||||
|
||||
|
||||
# Driver Code
|
||||
create_file()
|
||||
set_vers()
|
1
cmd/test_copy/date.txt
Normal file
1
cmd/test_copy/date.txt
Normal file
@ -0,0 +1 @@
|
||||
2022-03-14 10:13:39.265
|
1
cmd/test_copy/subversion.txt
Normal file
1
cmd/test_copy/subversion.txt
Normal file
@ -0,0 +1 @@
|
||||
00001
|
1
cmd/test_copy/version.txt
Normal file
1
cmd/test_copy/version.txt
Normal file
@ -0,0 +1 @@
|
||||
v0
|
BIN
docs/connections.jpg
Normal file
BIN
docs/connections.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
571
docs/packages.graphml
Normal file
571
docs/packages.graphml
Normal file
@ -0,0 +1,571 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.21.1-->
|
||||
<key for="port" id="d0" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d1" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d2" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d3"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d4"/>
|
||||
<key for="node" id="d5" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d6" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d7"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d8"/>
|
||||
<key for="edge" id="d9" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<node id="n0" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="550.562996031746" width="498.6220703125" x="-49.6220703125" y="-550.562996031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="7.1787109375" x="245.7216796875" xml:space="preserve" y="-15.640625">.</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="20.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="8.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="20.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0:">
|
||||
<node id="n0::n0" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="40.0" x="25.0" y="-27.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="25.5869140625" x="7.20654296875" xml:space="preserve" y="-15.640625">cmd</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="40.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="18.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="40.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n0:"/>
|
||||
</node>
|
||||
<node id="n0::n1" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="90.0" x="0.0" y="-84.64087301587301"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="52.427734375" x="18.7861328125" xml:space="preserve" y="-15.640625">examples</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="90.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="43.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="90.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n1:"/>
|
||||
</node>
|
||||
<node id="n0::n2" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="273.640625" width="436.0" x="-2.0" y="-403.922371031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="130.279296875" x="152.8603515625" xml:space="preserve" y="-15.640625">internal (2 func, 27 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="280.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="138.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="280.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n2:">
|
||||
<node id="n0::n2::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="64.0" x="13.0" y="-272.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="43.9765625" x="10.01171875" xml:space="preserve" y="1.6875">main</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n0::n2::n1" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="110.0" x="273.0" y="-201.28174603174602"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="122.3349609375" x="-6.16748046875" xml:space="preserve" y="-15.640625">config (3 func, 59 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="260.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="128.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="260.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n2::n1:">
|
||||
<node id="n0::n2::n1::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="80.0" x="288.0" y="-186.28174603174602"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="52.9609375" x="13.51953125" xml:space="preserve" y="1.6875">config</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n2::n2" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="102.0" x="105.0" y="-373.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="121.6171875" x="-9.80859375" xml:space="preserve" y="-15.640625">logic (9 func, 282 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="260.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="128.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="260.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n2::n2:">
|
||||
<node id="n0::n2::n2::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="72.0" x="120.0" y="-358.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="41.6328125" x="15.18359375" xml:space="preserve" y="1.6875">logic</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n2::n3" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="182.0" x="237.0" y="-373.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="173.365234375" x="4.3173828125" xml:space="preserve" y="-15.640625">packages_folder (3 func, 85 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="350.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="173.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="350.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n2::n3:">
|
||||
<node id="n0::n2::n3::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="152.0" x="252.0" y="-358.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="134.609375" x="8.6953125" xml:space="preserve" y="1.6875">packages_folder</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n2::n4" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="126.0" x="265.0" y="-287.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="143.51171875" x="-8.755859375" xml:space="preserve" y="-15.640625">parse_go (8 func, 187 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="290.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="143.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="290.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n2::n4:">
|
||||
<node id="n0::n2::n4::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="96.0" x="280.0" y="-272.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="76.6640625" x="9.66796875" xml:space="preserve" y="1.6875">parse_go</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n3" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="101.640625" width="450.74755859375" x="-34.6220703125" y="-519.922371031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="22.486328125" x="214.130615234375" xml:space="preserve" y="-15.640625">pkg</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="40.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="18.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="40.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n3:">
|
||||
<node id="n0::n3::n0" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="118.0" x="269.0" y="-489.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="146.2509765625" x="-14.12548828125" xml:space="preserve" y="-15.640625">graphml (14 func, 704 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="290.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="143.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="290.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n3::n0:">
|
||||
<node id="n0::n3::n0::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="88.0" x="284.0" y="-474.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="70.8671875" x="8.56640625" xml:space="preserve" y="1.6875">graphml</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n3::n1" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="94.0" x="-2.0" y="-489.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="129.244140625" x="-17.6220703125" xml:space="preserve" y="-15.640625">xgml (14 func, 311 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="260.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="128.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="260.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n3::n1:">
|
||||
<node id="n0::n3::n1::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="64.0" x="13.0" y="-474.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="43.65625" x="10.171875" xml:space="preserve" y="1.6875">xgml</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<node id="n0::n3::n2" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="56.0" width="86.0" x="113.0" y="-489.281746031746"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="15.640625" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="110.171875" x="-12.0859375" xml:space="preserve" y="-15.640625">xml (5 func, 50 lines)</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="230.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="113.0" y="-4.0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="12.0" closedWidth="230.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n0::n3::n2:">
|
||||
<node id="n0::n3::n2::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="56.0" x="128.0" y="-474.281746031746"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="33.5" x="11.25" xml:space="preserve" y="1.6875">xml</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="n0::n2::e0" source="n0::n2::n0" target="n0::n2::n1::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[main -> config]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="6.5">
|
||||
<y:Point x="166.0" y="-259.281746031746"/>
|
||||
<y:Point x="166.0" y="-166.78174603174602"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="130.1796875" xml:space="preserve" y="83.18749224950398">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::n2::e1" source="n0::n2::n0" target="n0::n2::n2::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[main -> logic]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="-8.666666666666686" tx="0.0" ty="0.0">
|
||||
<y:Point x="87.0" y="-267.9484126984127"/>
|
||||
<y:Point x="87.0" y="-345.281746031746"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="-5.291015625" xml:space="preserve" y="-47.979154072110646">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::n2::e2" source="n0::n2::n2::n0" target="n0::n2::n3::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[logic -> packages_folder]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="14.7421875" xml:space="preserve" y="-9.312507750496025">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::n2::e3" source="n0::n2::n2::n0" target="n0::n2::n4::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[logic -> parse_go]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="5.199999999999989" tx="0.0" ty="0.0">
|
||||
<y:Point x="227.0" y="-340.08174603174604"/>
|
||||
<y:Point x="227.0" y="-259.281746031746"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="19.72216796875" xml:space="preserve" y="31.087510560050873">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::n2::e4" source="n0::n2::n2::n0" target="n0::n2::n1::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[logic -> config]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="10.399999999999977" tx="0.0" ty="-6.5">
|
||||
<y:Point x="217.0" y="-334.88174603174605"/>
|
||||
<y:Point x="217.0" y="-179.78174603174602"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="9.73193359375" xml:space="preserve" y="68.23749835301959">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::e0" source="n0::n2::n2::n0" target="n0::n3::n0::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[logic -> graphml]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="-5.199999999999989" tx="0.0" ty="0.0">
|
||||
<y:Point x="227.0" y="-350.481746031746"/>
|
||||
<y:Point x="227.0" y="-461.281746031746"/>
|
||||
</y:Path>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="19.72216796875" xml:space="preserve" y="-64.7124955434648">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="n0::n3::e0" source="n0::n3::n1::n0" target="n0::n3::n2::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[xgml -> xml]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" bottomInset="0" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.625" horizontalTextPosition="center" iconTextGap="4" leftInset="0" modelName="centered" modelPosition="center" preferredPlacement="anywhere" ratio="0.5" rightInset="0" textColor="#000000" topInset="0" verticalTextPosition="bottom" visible="true" width="30.515625" x="10.2421875" xml:space="preserve" y="-9.312507750496025">
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d6">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
BIN
docs/packages.jpg
Normal file
BIN
docs/packages.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
28
go.mod
Normal file
28
go.mod
Normal file
@ -0,0 +1,28 @@
|
||||
module github.com/ManyakRus/image_database
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/ManyakRus/starter v0.0.0-20230720125411-d86504a07013
|
||||
github.com/beevik/etree v1.2.0
|
||||
golang.org/x/tools v0.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/ManyakRus/logrus v0.0.0-20230426064230-515895169d22 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.3.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/mdp/qrterminal/v3 v3.1.1 // indirect
|
||||
go.mau.fi/libsignal v0.1.0 // indirect
|
||||
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 // indirect
|
||||
go.mau.fi/whatsmeow v0.0.0-20230824151650-6da2abde6b7c // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
rsc.io/qr v0.2.0 // indirect
|
||||
)
|
61
go.sum
Normal file
61
go.sum
Normal file
@ -0,0 +1,61 @@
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
github.com/ManyakRus/logrus v0.0.0-20230426064230-515895169d22 h1:DrHNlqXfwvCpCqn4MfRn4NBtezqWL5GLor3jC7QFPj0=
|
||||
github.com/ManyakRus/logrus v0.0.0-20230426064230-515895169d22/go.mod h1:KbfWJjL1T+JHs/0tdcuqW6CKUakBEQ7oG9u5xpEfbTE=
|
||||
github.com/ManyakRus/starter v0.0.0-20230720125411-d86504a07013 h1:auTnvIiUr4hlaTGzrXQMLPT90oofEkCj5l3X5Zhu8Ms=
|
||||
github.com/ManyakRus/starter v0.0.0-20230720125411-d86504a07013/go.mod h1:wRsJrHV9PcbL8NSxOR14dnWUQ9MSraiZHw1uMQb/ojQ=
|
||||
github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw=
|
||||
github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mdp/qrterminal/v3 v3.1.1 h1:cIPwg3QU0OIm9+ce/lRfWXhPwEjOSKwk3HBwL3HBTyc=
|
||||
github.com/mdp/qrterminal/v3 v3.1.1/go.mod h1:5lJlXe7Jdr8wlPDdcsJttv1/knsRgzXASyr4dcGZqNU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c=
|
||||
go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I=
|
||||
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 h1:VrxDCO/gLFHLQywGUsJzertrvt2mUEMrZPf4hEL/s18=
|
||||
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
|
||||
go.mau.fi/whatsmeow v0.0.0-20230824151650-6da2abde6b7c h1:GH1QtL+bbkwyzgJBLyBQcr499oHR1y8svzB92KtG6OY=
|
||||
go.mau.fi/whatsmeow v0.0.0-20230824151650-6da2abde6b7c/go.mod h1:Iv3G4uv6+HWtqL7XSLRa2dSy077Bnji14IvqUbG+bRo=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss=
|
||||
golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
|
||||
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
|
69
internal/config/config.go
Normal file
69
internal/config/config.go
Normal file
@ -0,0 +1,69 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const FILENAME_GRAPHML = "connections.graphml"
|
||||
const SERVICE_NAME = "Main"
|
||||
|
||||
// Settings хранит все нужные переменные окружения
|
||||
var Settings SettingsINI
|
||||
|
||||
// SettingsINI - структура для хранения всех нужных переменных окружения
|
||||
type SettingsINI struct {
|
||||
DIRECTORY_SOURCE string
|
||||
FILENAME_GRAPHML string
|
||||
SERVICE_NAME string
|
||||
}
|
||||
|
||||
// FillSettings загружает переменные окружения в структуру из переменных окружения
|
||||
func FillSettings() {
|
||||
Settings = SettingsINI{}
|
||||
Settings.DIRECTORY_SOURCE = os.Getenv("DIRECTORY_SOURCE")
|
||||
Settings.FILENAME_GRAPHML = os.Getenv("FILENAME_GRAPHML")
|
||||
Settings.SERVICE_NAME = os.Getenv("SERVICE_NAME")
|
||||
|
||||
if Settings.DIRECTORY_SOURCE == "" {
|
||||
Settings.DIRECTORY_SOURCE = CurrentDirectory()
|
||||
//log.Panicln("Need fill DIRECTORY_SOURCE ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.FILENAME_GRAPHML == "" {
|
||||
Settings.FILENAME_GRAPHML = FILENAME_GRAPHML
|
||||
}
|
||||
|
||||
if Settings.SERVICE_NAME == "" {
|
||||
Settings.SERVICE_NAME = SERVICE_NAME
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
// CurrentDirectory - возвращает текущую директорию ОС
|
||||
func CurrentDirectory() string {
|
||||
Otvet, err := os.Getwd()
|
||||
if err != nil {
|
||||
//log.Println(err)
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// FillFlags - заполняет параметры из командной строки
|
||||
func FillFlags() {
|
||||
Args := os.Args[1:]
|
||||
if len(Args) > 3 {
|
||||
return
|
||||
}
|
||||
|
||||
if len(Args) > 0 {
|
||||
Settings.DIRECTORY_SOURCE = Args[0]
|
||||
}
|
||||
if len(Args) > 1 {
|
||||
Settings.FILENAME_GRAPHML = Args[1]
|
||||
}
|
||||
if len(Args) > 2 {
|
||||
Settings.SERVICE_NAME = Args[2]
|
||||
}
|
||||
}
|
1
internal/config/config_test.go
Normal file
1
internal/config/config_test.go
Normal file
@ -0,0 +1 @@
|
||||
package config
|
6
internal/constants/constants.go
Normal file
6
internal/constants/constants.go
Normal file
@ -0,0 +1,6 @@
|
||||
package constants
|
||||
|
||||
const TEXT_HELP = `
|
||||
Run the image_connections file with parameters:
|
||||
image_connections <your repository directory> <filename.graphml>
|
||||
`
|
381
internal/logic/logic.go
Normal file
381
internal/logic/logic.go
Normal file
@ -0,0 +1,381 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ManyakRus/image_database/internal/config"
|
||||
"github.com/ManyakRus/image_database/internal/packages_folder"
|
||||
"github.com/ManyakRus/image_database/internal/parse_go"
|
||||
"github.com/ManyakRus/image_database/pkg/graphml"
|
||||
"github.com/ManyakRus/starter/log"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"github.com/beevik/etree"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MapNameURL - связь URL и имени внешнего сервиса
|
||||
var MapServiceURL = make(map[string]string)
|
||||
|
||||
// MapPackagesElements - связь Пакета golang / Элемент файла .graphml
|
||||
var MapPackagesElements = make(map[*packages.Package]*etree.Element, 0)
|
||||
|
||||
// MapServiceNameElements - связь ИД Пакета golang / Элемент файла .graphml
|
||||
var MapServiceNameElements = make(map[string]*etree.Element, 0)
|
||||
|
||||
func StartFillAll(FileName string) bool {
|
||||
Otvet := false
|
||||
|
||||
//RepositoryName := FindRepositoryName()
|
||||
RepositoryName := config.Settings.SERVICE_NAME
|
||||
|
||||
DocXML, ElementGraph := graphml.CreateDocument()
|
||||
|
||||
//рисуем элемент
|
||||
ElementShapeMain := graphml.CreateElement_Shape(ElementGraph, RepositoryName)
|
||||
|
||||
//
|
||||
MassPackages := packages_folder.FindMassPackageFromFolder(config.Settings.DIRECTORY_SOURCE)
|
||||
FillPackage(MassPackages, ElementGraph)
|
||||
|
||||
//заполним связи
|
||||
log.Info("Start fill links")
|
||||
FillLinks(ElementGraph, ElementShapeMain)
|
||||
|
||||
if len(MapServiceNameElements) > 0 {
|
||||
Otvet = true
|
||||
}
|
||||
|
||||
if Otvet == false {
|
||||
println("warning: Empty file not saved !")
|
||||
return Otvet
|
||||
}
|
||||
|
||||
log.Info("Start save file")
|
||||
DocXML.Indent(2)
|
||||
err := DocXML.WriteToFile(FileName)
|
||||
if err != nil {
|
||||
log.Error("WriteToFile() FileName: ", FileName, " error: ", err)
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// FillLinks - заполняет связи (стрелки) между пакетами
|
||||
func FillLinks(ElementGraph, ElementShapeMain *etree.Element) {
|
||||
for ServiceName, ElementTo := range MapServiceNameElements {
|
||||
descr := config.Settings.SERVICE_NAME + " -> " + ServiceName
|
||||
graphml.CreateElement_Edge(ElementGraph, ElementShapeMain, ElementTo, "", descr)
|
||||
}
|
||||
}
|
||||
|
||||
// FillLinks_goroutine - заполняет связи (стрелки) между пакетами для горутин go, синим цветом
|
||||
func FillLinks_goroutine(ElementGraph *etree.Element) {
|
||||
for PackageFrom, ElementFrom := range MapPackagesElements {
|
||||
for _, Filename1 := range PackageFrom.GoFiles {
|
||||
|
||||
AstFile, err := parse_go.ParseFile(Filename1)
|
||||
if err != nil {
|
||||
log.Warn("ParseFile() ", Filename1, " error: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
MassGoImport := parse_go.FindGo(AstFile)
|
||||
for _, GoImport1 := range MassGoImport {
|
||||
//Go_package_name := GoImport1.Go_package_name
|
||||
Go_package_import := GoImport1.Go_package_import
|
||||
Go_func_name := GoImport1.Go_func_name
|
||||
|
||||
ElementImport, ok := MapServiceNameElements[Go_package_import]
|
||||
if ok == false {
|
||||
//посторонние импорты
|
||||
//continue
|
||||
ElementImport = ElementFrom
|
||||
}
|
||||
label := Go_func_name
|
||||
descr := PackageFrom.Name + " -> " + GoImport1.Go_package_name
|
||||
graphml.CreateElement_Edge_blue(ElementGraph, ElementFrom, ElementImport, label, descr)
|
||||
}
|
||||
|
||||
//ElementImport, ok := MapServiceNameElements[PackageImport.ID]
|
||||
//if ok == false {
|
||||
// //посторонние импорты
|
||||
// //log.Panic("MapPackagesElements[PackageImport] error: ok =false")
|
||||
// continue
|
||||
//}
|
||||
//graphml.CreateElement_Edge(ElementGraph, ElementFrom.Index(), ElementImport.Index())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func FillPackage(MassPackages []*packages.Package, ElementGraph *etree.Element) {
|
||||
|
||||
for _, v := range MassPackages {
|
||||
PackageName := v.PkgPath
|
||||
|
||||
////test
|
||||
//pos1 := strings.Index(PackageName, "camunda")
|
||||
//if pos1 > 0 {
|
||||
// print("test")
|
||||
//}
|
||||
|
||||
ServiceName := FindNeedShow(PackageName)
|
||||
if ServiceName != "" {
|
||||
//проверка уже нарисован
|
||||
_, ok := MapServiceNameElements[ServiceName]
|
||||
if ok == true {
|
||||
//уже нарисован
|
||||
continue
|
||||
}
|
||||
|
||||
//рисуем элемент
|
||||
ElementShape := graphml.CreateElement_Shape(ElementGraph, ServiceName)
|
||||
MapPackagesElements[v] = ElementShape
|
||||
MapServiceNameElements[ServiceName] = ElementShape
|
||||
|
||||
//
|
||||
continue
|
||||
}
|
||||
|
||||
//рекурсия всех импортов пакета
|
||||
if len(v.Imports) > 0 {
|
||||
// Convert map to slice of MassPackagesImports.
|
||||
MassPackagesImports := []*packages.Package{}
|
||||
for _, value := range v.Imports {
|
||||
pos1 := strings.Index(value.PkgPath, ".")
|
||||
//pos2 := strings.Index(value.PkgPath, "\\")
|
||||
if pos1 > 0 {
|
||||
MassPackagesImports = append(MassPackagesImports, value)
|
||||
}
|
||||
}
|
||||
|
||||
FillPackage(MassPackagesImports, ElementGraph)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func FindNeedShow(PackageURL string) string {
|
||||
Otvet := ""
|
||||
|
||||
lenPackage := len(PackageURL)
|
||||
|
||||
for URL, ServiceName := range MapServiceURL {
|
||||
lenConn := len(URL)
|
||||
if lenConn > lenPackage {
|
||||
continue
|
||||
}
|
||||
if URL != PackageURL[:lenConn] {
|
||||
continue
|
||||
}
|
||||
Otvet = ServiceName
|
||||
break
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
//func FillFolder(ElementGraph, ElementGroup *etree.Element, Folder *folders.Folder) {
|
||||
//
|
||||
// FolderName := Folder.Name
|
||||
//
|
||||
// PackageFolder1 := packages_folder.FindPackageFromFolder(Folder)
|
||||
// PackageName := PackageFolder1.Name
|
||||
// if PackageName == "" && len(Folder.Folders) == 0 {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// GroupName := FolderName
|
||||
// lines_count, func_count := FindLinesCount_package(PackageFolder1.Package)
|
||||
// if lines_count > 0 || func_count > 0 {
|
||||
// GroupName = GroupName + " ("
|
||||
// if func_count > 0 {
|
||||
// GroupName = GroupName + strconv.Itoa(func_count) + " func"
|
||||
// GroupName = GroupName + ", "
|
||||
// }
|
||||
// if lines_count > 0 {
|
||||
// GroupName = GroupName + strconv.Itoa(lines_count) + " lines"
|
||||
// }
|
||||
// GroupName = GroupName + ")"
|
||||
// }
|
||||
//
|
||||
// //добавим группа (каталог)
|
||||
// var ElementGroup2 *etree.Element
|
||||
// if ElementGroup != nil {
|
||||
// ElementGroup2 = graphml.CreateElement_Group(ElementGroup, GroupName)
|
||||
// } else {
|
||||
// ElementGroup2 = graphml.CreateElement_Group(ElementGraph, GroupName)
|
||||
// }
|
||||
// if PackageName != "" {
|
||||
// //добавим пакет(package)
|
||||
// ElementShape := graphml.CreateElement_Shape(ElementGroup2, PackageName)
|
||||
// MapPackagesElements[PackageFolder1.Package] = ElementShape
|
||||
// MapServiceNameElements[PackageFolder1.Package.ID] = ElementShape
|
||||
// //MapPackagesElements[&PackageFolder1] = ElementShape
|
||||
// }
|
||||
//
|
||||
// //сортировка
|
||||
// MassKeys := make([]string, 0, len(Folder.Folders))
|
||||
// for k := range Folder.Folders {
|
||||
// MassKeys = append(MassKeys, k)
|
||||
// }
|
||||
// sort.Strings(MassKeys)
|
||||
//
|
||||
// //обход всех папок
|
||||
// for _, key1 := range MassKeys {
|
||||
// Folder1, ok := Folder.Folders[key1]
|
||||
// if ok == false {
|
||||
// log.Panic("Folder.Folders[key1] ok =false")
|
||||
// }
|
||||
// FillFolder(ElementGraph, ElementGroup2, Folder1)
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
//// FindFileNameShort - возвращает имя файла(каталога) без пути
|
||||
//func FindFileNameShort(path string) string {
|
||||
// Otvet := ""
|
||||
// if path == "" {
|
||||
// return Otvet
|
||||
// }
|
||||
// Otvet = filepath.Base(path)
|
||||
//
|
||||
// return Otvet
|
||||
//}
|
||||
|
||||
func FindLinesCount_package(Package1 *packages.Package) (int, int) {
|
||||
LinesCount := 0
|
||||
FuncCount := 0
|
||||
|
||||
if Package1 == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
if Package1.GoFiles == nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for _, s := range Package1.GoFiles {
|
||||
count, func_count := FindLinesCount(s)
|
||||
LinesCount = LinesCount + count
|
||||
FuncCount = FuncCount + func_count
|
||||
}
|
||||
|
||||
return LinesCount, FuncCount
|
||||
}
|
||||
|
||||
func FindLinesCount(FileName string) (int, int) {
|
||||
LinesCount := 0
|
||||
FuncCount := 0
|
||||
|
||||
bytes1, err := os.ReadFile(FileName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(bytes1)
|
||||
LinesCount, err = LinesCount_reader(reader)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
FuncCount = FindFuncCount(&bytes1)
|
||||
|
||||
return LinesCount, FuncCount
|
||||
}
|
||||
|
||||
func LinesCount_reader(r io.Reader) (int, error) {
|
||||
Otvet := 0
|
||||
var err error
|
||||
|
||||
buf := make([]byte, 8192)
|
||||
|
||||
for {
|
||||
c, err := r.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF && c == 0 {
|
||||
break
|
||||
} else {
|
||||
return Otvet, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, b := range buf[:c] {
|
||||
if b == '\n' {
|
||||
Otvet++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return Otvet, err
|
||||
}
|
||||
|
||||
// FindFuncCount - находит количество функций(func) в файле
|
||||
func FindFuncCount(bytes *[]byte) int {
|
||||
Otvet := 0
|
||||
|
||||
s := string(*bytes)
|
||||
sFind := "(\n|\t| )func( |\t)"
|
||||
|
||||
Otvet = CountMatches(s, regexp.MustCompile(sFind))
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// CountMatches - находит количество совпадений в regexp
|
||||
func CountMatches(s string, re *regexp.Regexp) int {
|
||||
total := 0
|
||||
for start := 0; start < len(s); {
|
||||
remaining := s[start:] // slicing the string is cheap
|
||||
loc := re.FindStringIndex(remaining)
|
||||
if loc == nil {
|
||||
break
|
||||
}
|
||||
// loc[0] is the start index of the match,
|
||||
// loc[1] is the end index (exclusive)
|
||||
start += loc[1]
|
||||
total++
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// FindRepositoryName - находит имя папки (репозитория)
|
||||
func FindRepositoryName() string {
|
||||
Otvet := ""
|
||||
|
||||
dir := config.Settings.DIRECTORY_SOURCE
|
||||
if dir == "" {
|
||||
return Otvet
|
||||
}
|
||||
if dir == "./" {
|
||||
dir = micro.ProgramDir()
|
||||
}
|
||||
dir = DeleteFileSeperator(dir)
|
||||
Otvet = path.Base(dir)
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// DeleteFileSeperator - убирает в конце / или \
|
||||
func DeleteFileSeperator(dir string) string {
|
||||
Otvet := dir
|
||||
|
||||
len1 := len(Otvet)
|
||||
if len1 == 0 {
|
||||
return Otvet
|
||||
}
|
||||
|
||||
LastWord := Otvet[len1-1 : len1]
|
||||
if LastWord == micro.SeparatorFile() {
|
||||
Otvet = Otvet[0 : len1-1]
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
27
internal/logic/logic_test.go
Normal file
27
internal/logic/logic_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/image_database/internal/config"
|
||||
ConfigMain "github.com/ManyakRus/starter/config"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStartFillAll(t *testing.T) {
|
||||
ConfigMain.LoadEnv()
|
||||
config.FillSettings()
|
||||
|
||||
dir := micro.ProgramDir()
|
||||
FileName := dir + "test" + micro.SeparatorFile() + "test_start.xgml"
|
||||
StartFillAll(FileName)
|
||||
}
|
||||
|
||||
func TestFindRepositoryName(t *testing.T) {
|
||||
ConfigMain.LoadEnv()
|
||||
config.FillSettings()
|
||||
|
||||
Otvet := FindRepositoryName()
|
||||
if Otvet == "" {
|
||||
t.Error("TestFindRepositoryName() error: =''")
|
||||
}
|
||||
}
|
37
internal/main.go
Normal file
37
internal/main.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/image_database/internal/config"
|
||||
"github.com/ManyakRus/image_database/internal/constants"
|
||||
"github.com/ManyakRus/image_database/internal/load_json"
|
||||
"github.com/ManyakRus/image_database/internal/logic"
|
||||
ConfigMain "github.com/ManyakRus/starter/config"
|
||||
"github.com/ManyakRus/starter/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
StartApp()
|
||||
}
|
||||
|
||||
func StartApp() {
|
||||
ConfigMain.LoadEnv()
|
||||
config.FillSettings()
|
||||
config.FillFlags()
|
||||
|
||||
load_json.LoadJSON()
|
||||
|
||||
FileName := config.Settings.FILENAME_GRAPHML
|
||||
log.Info("directory: ", config.Settings.DIRECTORY_SOURCE)
|
||||
log.Info("file graphml: ", FileName)
|
||||
log.Info("service name: ", config.Settings.SERVICE_NAME)
|
||||
ok := logic.StartFillAll(FileName)
|
||||
if ok == false {
|
||||
println(constants.TEXT_HELP)
|
||||
}
|
||||
|
||||
////test
|
||||
//test1 := postgres_connect.Settings.DB_HOST
|
||||
//test2 := whatsapp_connect.Settings.WHATSAPP_PHONE_FROM
|
||||
//if test1+test2 == "" {
|
||||
//}
|
||||
}
|
9
internal/main_test.go
Normal file
9
internal/main_test.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStartApp(t *testing.T) {
|
||||
StartApp()
|
||||
}
|
704
pkg/graphml/graphml.go
Normal file
704
pkg/graphml/graphml.go
Normal file
@ -0,0 +1,704 @@
|
||||
package graphml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/beevik/etree"
|
||||
_ "github.com/beevik/etree"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FONT_SIZE_SHAPE - размер шрифта прямоугольника
|
||||
var FONT_SIZE_SHAPE = 16
|
||||
|
||||
// FONT_SIZE_SHAPE - размер шрифта групп
|
||||
var FONT_SIZE_GROUP = 10
|
||||
|
||||
// FONT_SIZE_EDGE - размер шрифта стрелок
|
||||
var FONT_SIZE_EDGE = 8
|
||||
|
||||
//var doc = etree.NewDocument()
|
||||
|
||||
// CreateElement_Shape - создаёт элемент xgml - прямоугольник
|
||||
func CreateElement_Shape(ElementGraph0 *etree.Element, ElementName string) *etree.Element {
|
||||
|
||||
Width := findWidth_Shape(ElementName)
|
||||
Height := findHeight_Shape(ElementName)
|
||||
sWidth := fmt.Sprintf("%.1f", float32(Width))
|
||||
sHeight := fmt.Sprintf("%.1f", float32(Height))
|
||||
|
||||
sFontSize := strconv.Itoa(FONT_SIZE_SHAPE)
|
||||
|
||||
//ищем graph
|
||||
var ElementGraph *etree.Element
|
||||
ElementGraph2 := ElementGraph0.SelectElement("graph")
|
||||
if ElementGraph2 != nil {
|
||||
ElementGraph = ElementGraph2
|
||||
} else {
|
||||
ElementGraph = ElementGraph0
|
||||
}
|
||||
|
||||
//node
|
||||
ElementNode := ElementGraph.CreateElement("node")
|
||||
sId := FindId(ElementGraph0, ElementNode)
|
||||
ElementNode.CreateAttr("id", sId)
|
||||
//ElementNode.CreateAttr("id", "n"+strconv.Itoa(ElementNode.Index()))
|
||||
|
||||
//data
|
||||
ElementData := ElementNode.CreateElement("data")
|
||||
ElementData.CreateAttr("key", "d5")
|
||||
|
||||
//ShapeNode
|
||||
ElementShapeNode := ElementData.CreateElement("y:ShapeNode")
|
||||
|
||||
//YGeometry
|
||||
ElementYGeometry := ElementShapeNode.CreateElement("y:Geometry")
|
||||
ElementYGeometry.CreateAttr("height", sHeight)
|
||||
ElementYGeometry.CreateAttr("width", sWidth)
|
||||
ElementYGeometry.CreateAttr("x", "0.0")
|
||||
ElementYGeometry.CreateAttr("y", "0.0")
|
||||
|
||||
//YFill
|
||||
ElementYFill := ElementShapeNode.CreateElement("y:Fill")
|
||||
ElementYFill.CreateAttr("color", "#FFFFFF")
|
||||
ElementYFill.CreateAttr("transparent", "false")
|
||||
|
||||
//BorderStyle
|
||||
ElementBorderStyle := ElementShapeNode.CreateElement("y:BorderStyle")
|
||||
ElementBorderStyle.CreateAttr("color", "#000000")
|
||||
ElementBorderStyle.CreateAttr("type", "line")
|
||||
ElementBorderStyle.CreateAttr("width", "1.0")
|
||||
|
||||
//NodeLabel
|
||||
ElementNodeLabel := ElementShapeNode.CreateElement("y:NodeLabel")
|
||||
ElementNodeLabel.CreateAttr("alignment", "center")
|
||||
ElementNodeLabel.CreateAttr("autoSizePolicy", "content")
|
||||
ElementNodeLabel.CreateAttr("fontFamily", "Dialog")
|
||||
ElementNodeLabel.CreateAttr("fontSize", sFontSize)
|
||||
ElementNodeLabel.CreateAttr("fontStyle", "plain")
|
||||
ElementNodeLabel.CreateAttr("hasBackgroundColor", "false")
|
||||
ElementNodeLabel.CreateAttr("hasLineColor", "false")
|
||||
ElementNodeLabel.CreateAttr("height", sHeight)
|
||||
ElementNodeLabel.CreateAttr("horizontalTextPosition", "center")
|
||||
ElementNodeLabel.CreateAttr("iconTextGap", "4")
|
||||
ElementNodeLabel.CreateAttr("modelName", "internal")
|
||||
ElementNodeLabel.CreateAttr("modelPosition", "c")
|
||||
ElementNodeLabel.CreateAttr("textColor", "#000000")
|
||||
ElementNodeLabel.CreateAttr("verticalTextPosition", "bottom")
|
||||
ElementNodeLabel.CreateAttr("visible", "true")
|
||||
ElementNodeLabel.CreateAttr("width", sWidth)
|
||||
ElementNodeLabel.CreateAttr("x", "0.0")
|
||||
ElementNodeLabel.CreateAttr("xml:space", "preserve")
|
||||
ElementNodeLabel.CreateAttr("y", "0.0")
|
||||
ElementNodeLabel.CreateText(ElementName)
|
||||
|
||||
//y:Shape
|
||||
ElementYShape := ElementShapeNode.CreateElement("y:Shape")
|
||||
ElementYShape.CreateAttr("type", "rectangle")
|
||||
|
||||
return ElementNode
|
||||
}
|
||||
|
||||
// CreateElement_Group - создаёт элемент xgml - группа
|
||||
func CreateElement_Group(ElementGraph0 *etree.Element, GroupCaption string) *etree.Element {
|
||||
|
||||
Width := findWidth_Group(GroupCaption)
|
||||
Height := findHeight_Group(GroupCaption)
|
||||
sWidth := fmt.Sprintf("%.1f", float32(Width))
|
||||
sHeight := fmt.Sprintf("%.1f", float32(Height))
|
||||
|
||||
//ищем graph
|
||||
var ElementGraph *etree.Element
|
||||
ElementGraph2 := ElementGraph0.SelectElement("graph")
|
||||
if ElementGraph2 != nil {
|
||||
ElementGraph = ElementGraph2
|
||||
} else {
|
||||
ElementGraph = ElementGraph0
|
||||
}
|
||||
|
||||
//node
|
||||
ElementNode := ElementGraph.CreateElement("node")
|
||||
//NodeId := "n" + strconv.Itoa(ElementNode.Index())
|
||||
NodeId := FindId(ElementGraph, ElementNode)
|
||||
ElementNode.CreateAttr("id", NodeId)
|
||||
ElementNode.CreateAttr("yfiles.foldertype", "group")
|
||||
|
||||
//data
|
||||
ElementData := ElementNode.CreateElement("data")
|
||||
ElementData.CreateAttr("key", "d5")
|
||||
|
||||
//YProxyAutoBoundsNode
|
||||
ElementYProxyAutoBoundsNode := ElementData.CreateElement("y:ProxyAutoBoundsNode")
|
||||
|
||||
//YRealizers
|
||||
ElementYRealizers := ElementYProxyAutoBoundsNode.CreateElement("y:Realizers")
|
||||
ElementYRealizers.CreateAttr("active", "0")
|
||||
|
||||
//----------------------- visible ---------------------------------------------
|
||||
|
||||
//YGroupNode
|
||||
ElementYGroupNode := ElementYRealizers.CreateElement("y:GroupNode")
|
||||
|
||||
//YGeometry
|
||||
ElementYGeometry := ElementYGroupNode.CreateElement("y:Geometry")
|
||||
ElementYGeometry.CreateAttr("height", sHeight)
|
||||
ElementYGeometry.CreateAttr("width", sWidth)
|
||||
ElementYGeometry.CreateAttr("x", "0.0")
|
||||
ElementYGeometry.CreateAttr("y", "0.0")
|
||||
|
||||
//YFill
|
||||
ElementYFill := ElementYGroupNode.CreateElement("y:Fill")
|
||||
ElementYFill.CreateAttr("color", "#F5F5F5")
|
||||
ElementYFill.CreateAttr("transparent", "false")
|
||||
|
||||
//YBorderStyle
|
||||
ElementYBorderStyle := ElementYGroupNode.CreateElement("y:BorderStyle")
|
||||
ElementYBorderStyle.CreateAttr("color", "#000000")
|
||||
ElementYBorderStyle.CreateAttr("type", "dashed")
|
||||
ElementYBorderStyle.CreateAttr("width", "1.0")
|
||||
|
||||
//YNodeLabel
|
||||
ElementYNodeLabel := ElementYGroupNode.CreateElement("y:NodeLabel")
|
||||
ElementYNodeLabel.CreateAttr("alignment", "right")
|
||||
ElementYNodeLabel.CreateAttr("autoSizePolicy", "content")
|
||||
ElementYNodeLabel.CreateAttr("backgroundColor", "#EBEBEB")
|
||||
ElementYNodeLabel.CreateAttr("borderDistance", "0.0")
|
||||
ElementYNodeLabel.CreateAttr("fontFamily", "Dialog")
|
||||
ElementYNodeLabel.CreateAttr("fontSize", strconv.Itoa(FONT_SIZE_GROUP))
|
||||
ElementYNodeLabel.CreateAttr("fontStyle", "plain")
|
||||
ElementYNodeLabel.CreateAttr("hasLineColor", "false")
|
||||
ElementYNodeLabel.CreateAttr("height", sHeight)
|
||||
ElementYNodeLabel.CreateAttr("horizontalTextPosition", "center")
|
||||
ElementYNodeLabel.CreateAttr("iconTextGap", "4")
|
||||
ElementYNodeLabel.CreateAttr("modelName", "sandwich")
|
||||
ElementYNodeLabel.CreateAttr("modelPosition", "n")
|
||||
ElementYNodeLabel.CreateAttr("textColor", "#000000")
|
||||
ElementYNodeLabel.CreateAttr("verticalTextPosition", "bottom")
|
||||
ElementYNodeLabel.CreateAttr("width", sWidth)
|
||||
ElementYNodeLabel.CreateAttr("x", "0")
|
||||
ElementYNodeLabel.CreateAttr("xml:space", "preserve")
|
||||
ElementYNodeLabel.CreateAttr("y", "0")
|
||||
ElementYNodeLabel.CreateText(GroupCaption)
|
||||
|
||||
//YShape
|
||||
ElementYShape := ElementYGroupNode.CreateElement("y:Shape")
|
||||
ElementYShape.CreateAttr("type", "roundrectangle")
|
||||
|
||||
//YState
|
||||
ElementYState := ElementYGroupNode.CreateElement("y:State")
|
||||
ElementYState.CreateAttr("closed", "false")
|
||||
ElementYState.CreateAttr("closedHeight", "80.0")
|
||||
ElementYState.CreateAttr("closedWidth", "100.0")
|
||||
ElementYState.CreateAttr("innerGraphDisplayEnabled", "false")
|
||||
|
||||
//YInsets
|
||||
ElementYInsets := ElementYGroupNode.CreateElement("y:Insets")
|
||||
ElementYInsets.CreateAttr("bottom", "15")
|
||||
ElementYInsets.CreateAttr("bottomF", "15.0")
|
||||
ElementYInsets.CreateAttr("left", "15")
|
||||
ElementYInsets.CreateAttr("leftF", "15.0")
|
||||
ElementYInsets.CreateAttr("right", "15")
|
||||
ElementYInsets.CreateAttr("rightF", "15.0")
|
||||
ElementYInsets.CreateAttr("top", "15")
|
||||
ElementYInsets.CreateAttr("topF", "15.0")
|
||||
|
||||
//YBorderInsets
|
||||
ElementYBorderInsets := ElementYGroupNode.CreateElement("y:BorderInsets")
|
||||
ElementYBorderInsets.CreateAttr("bottom", "54")
|
||||
ElementYBorderInsets.CreateAttr("bottomF", "54.0")
|
||||
ElementYBorderInsets.CreateAttr("left", "0")
|
||||
ElementYBorderInsets.CreateAttr("leftF", "0.0")
|
||||
ElementYBorderInsets.CreateAttr("right", "23")
|
||||
ElementYBorderInsets.CreateAttr("rightF", "23.35")
|
||||
ElementYBorderInsets.CreateAttr("top", "0")
|
||||
ElementYBorderInsets.CreateAttr("topF", "0.0")
|
||||
|
||||
//----------------------- not visible ---------------------------------------------
|
||||
|
||||
//YGroupNode
|
||||
ElementYGroupNode2 := ElementYRealizers.CreateElement("y:GroupNode")
|
||||
|
||||
//YGeometry
|
||||
ElementYGeometry2 := ElementYGroupNode2.CreateElement("y:Geometry")
|
||||
ElementYGeometry2.CreateAttr("height", sHeight)
|
||||
ElementYGeometry2.CreateAttr("width", sWidth)
|
||||
ElementYGeometry2.CreateAttr("x", "0.0")
|
||||
ElementYGeometry2.CreateAttr("y", "0.0")
|
||||
|
||||
//YFill
|
||||
ElementYFill2 := ElementYGroupNode2.CreateElement("y:Fill")
|
||||
ElementYFill2.CreateAttr("color", "#F5F5F5")
|
||||
ElementYFill2.CreateAttr("transparent", "false")
|
||||
|
||||
//YBorderStyle
|
||||
ElementYBorderStyle2 := ElementYGroupNode2.CreateElement("y:BorderStyle")
|
||||
ElementYBorderStyle2.CreateAttr("color", "#000000")
|
||||
ElementYBorderStyle2.CreateAttr("type", "dashed")
|
||||
ElementYBorderStyle2.CreateAttr("width", "1.0")
|
||||
|
||||
//YNodeLabel
|
||||
ElementYNodeLabel2 := ElementYGroupNode2.CreateElement("y:NodeLabel")
|
||||
ElementYNodeLabel2.CreateAttr("alignment", "right")
|
||||
ElementYNodeLabel2.CreateAttr("autoSizePolicy", "content")
|
||||
ElementYNodeLabel2.CreateAttr("backgroundColor", "#EBEBEB")
|
||||
ElementYNodeLabel2.CreateAttr("borderDistance", "0.0")
|
||||
ElementYNodeLabel2.CreateAttr("fontFamily", "Dialog")
|
||||
ElementYNodeLabel2.CreateAttr("fontSize", strconv.Itoa(FONT_SIZE_GROUP))
|
||||
ElementYNodeLabel2.CreateAttr("fontStyle", "plain")
|
||||
ElementYNodeLabel2.CreateAttr("hasLineColor", "false")
|
||||
ElementYNodeLabel2.CreateAttr("hasText", "false") //только у 2
|
||||
ElementYNodeLabel2.CreateAttr("height", sHeight)
|
||||
ElementYNodeLabel2.CreateAttr("horizontalTextPosition", "center")
|
||||
ElementYNodeLabel2.CreateAttr("iconTextGap", "4")
|
||||
ElementYNodeLabel2.CreateAttr("modelName", "sandwich")
|
||||
ElementYNodeLabel2.CreateAttr("modelPosition", "n")
|
||||
ElementYNodeLabel2.CreateAttr("textColor", "#000000")
|
||||
ElementYNodeLabel2.CreateAttr("verticalTextPosition", "bottom")
|
||||
ElementYNodeLabel2.CreateAttr("width", sWidth)
|
||||
ElementYNodeLabel2.CreateAttr("x", "0")
|
||||
//ElementYNodeLabel2.CreateAttr("xml:space", "preserve") //только у 2
|
||||
ElementYNodeLabel2.CreateAttr("y", "0")
|
||||
//ElementYNodeLabel2.CreateText(GroupCaption) //только у 2
|
||||
|
||||
//YShape
|
||||
ElementYShape2 := ElementYGroupNode2.CreateElement("y:Shape")
|
||||
ElementYShape2.CreateAttr("type", "roundrectangle")
|
||||
|
||||
//YState
|
||||
ElementYState2 := ElementYGroupNode2.CreateElement("y:State")
|
||||
ElementYState2.CreateAttr("closed", "true")
|
||||
ElementYState2.CreateAttr("closedHeight", "80.0")
|
||||
ElementYState2.CreateAttr("closedWidth", "100.0")
|
||||
ElementYState2.CreateAttr("innerGraphDisplayEnabled", "false")
|
||||
|
||||
//YInsets
|
||||
ElementYInsets2 := ElementYGroupNode2.CreateElement("y:Insets")
|
||||
ElementYInsets2.CreateAttr("bottom", "15")
|
||||
ElementYInsets2.CreateAttr("bottomF", "15.0")
|
||||
ElementYInsets2.CreateAttr("left", "15")
|
||||
ElementYInsets2.CreateAttr("leftF", "15.0")
|
||||
ElementYInsets2.CreateAttr("right", "15")
|
||||
ElementYInsets2.CreateAttr("rightF", "15.0")
|
||||
ElementYInsets2.CreateAttr("top", "15")
|
||||
ElementYInsets2.CreateAttr("topF", "15.0")
|
||||
|
||||
//YBorderInsets
|
||||
ElementYBorderInsets2 := ElementYGroupNode2.CreateElement("y:BorderInsets")
|
||||
ElementYBorderInsets2.CreateAttr("bottom", "54")
|
||||
ElementYBorderInsets2.CreateAttr("bottomF", "54.0")
|
||||
ElementYBorderInsets2.CreateAttr("left", "0")
|
||||
ElementYBorderInsets2.CreateAttr("leftF", "0.0")
|
||||
ElementYBorderInsets2.CreateAttr("right", "23")
|
||||
ElementYBorderInsets2.CreateAttr("rightF", "23.35")
|
||||
ElementYBorderInsets2.CreateAttr("top", "0")
|
||||
ElementYBorderInsets2.CreateAttr("topF", "0.0")
|
||||
|
||||
//----------------------- продолжение ---------------------------------------------
|
||||
//YBorderInsets
|
||||
ElementGraphGraph := ElementNode.CreateElement("graph")
|
||||
ElementGraphGraph.CreateAttr("edgedefault", "directed")
|
||||
ElementGraphGraph.CreateAttr("id", NodeId+":")
|
||||
|
||||
return ElementNode
|
||||
}
|
||||
|
||||
// CreateElement_Edge - создаёт элемент xgml - стрелка
|
||||
func CreateElement_Edge(ElementGraph, ElementFrom, ElementTo *etree.Element, label, Description string) *etree.Element {
|
||||
|
||||
//node
|
||||
ElementEdge := ElementGraph.CreateElement("edge")
|
||||
//EdgeId := FindId(ElementGraph, ElementEdge)
|
||||
//EdgeID := EdgeId
|
||||
EdgeID := "e" + strconv.Itoa(ElementEdge.Index())
|
||||
ElementEdge.CreateAttr("id", EdgeID)
|
||||
//Source := "n" + strconv.Itoa(IndexElementFrom) + "::" + "n" + strconv.Itoa(IndexElementTo)
|
||||
IdFrom := FindId(ElementGraph, ElementFrom)
|
||||
IdTo := FindId(ElementGraph, ElementTo)
|
||||
ElementEdge.CreateAttr("source", IdFrom)
|
||||
ElementEdge.CreateAttr("target", IdTo)
|
||||
|
||||
//data
|
||||
ElementData := ElementEdge.CreateElement("data")
|
||||
ElementData.CreateAttr("key", "d8")
|
||||
ElementData.CreateAttr("xml:space", "preserve")
|
||||
//ElementData.CreateText("<![CDATA[descr]]>")
|
||||
//ElementData.CreateElement("![CDATA[descr]]")
|
||||
ElementData.CreateCData(Description)
|
||||
|
||||
//data2
|
||||
ElementData2 := ElementEdge.CreateElement("data")
|
||||
ElementData2.CreateAttr("key", "d9")
|
||||
|
||||
//y:PolyLineEdge
|
||||
ElementYPolyLineEdge := ElementData2.CreateElement("y:PolyLineEdge")
|
||||
|
||||
//y:Path
|
||||
ElementYPath := ElementYPolyLineEdge.CreateElement("y:Path")
|
||||
ElementYPath.CreateAttr("sx", "0.0")
|
||||
ElementYPath.CreateAttr("sy", "0.0")
|
||||
ElementYPath.CreateAttr("tx", "0.0")
|
||||
ElementYPath.CreateAttr("ty", "0.0")
|
||||
|
||||
//y:LineStyle
|
||||
ElementYLineStyle := ElementYPolyLineEdge.CreateElement("y:LineStyle")
|
||||
ElementYLineStyle.CreateAttr("color", "#000000")
|
||||
ElementYLineStyle.CreateAttr("type", "line")
|
||||
ElementYLineStyle.CreateAttr("width", "1.0")
|
||||
|
||||
//y:Arrows
|
||||
ElementYArrows := ElementYPolyLineEdge.CreateElement("y:Arrows")
|
||||
ElementYArrows.CreateAttr("source", "standard")
|
||||
ElementYArrows.CreateAttr("target", "standard")
|
||||
|
||||
//y:EdgeLabel
|
||||
ElementYEdgeLabel := ElementYPolyLineEdge.CreateElement("y:EdgeLabel")
|
||||
ElementYEdgeLabel.CreateAttr("alignment", "center")
|
||||
ElementYEdgeLabel.CreateAttr("configuration", "AutoFlippingLabel")
|
||||
ElementYEdgeLabel.CreateAttr("distance", "0.0")
|
||||
ElementYEdgeLabel.CreateAttr("fontFamily", "Dialog")
|
||||
ElementYEdgeLabel.CreateAttr("fontSize", strconv.Itoa(FONT_SIZE_EDGE))
|
||||
ElementYEdgeLabel.CreateAttr("fontStyle", "plain")
|
||||
ElementYEdgeLabel.CreateAttr("hasBackgroundColor", "false")
|
||||
ElementYEdgeLabel.CreateAttr("hasLineColor", "false")
|
||||
ElementYEdgeLabel.CreateAttr("height", "17.96875")
|
||||
ElementYEdgeLabel.CreateAttr("horizontalTextPosition", "center")
|
||||
ElementYEdgeLabel.CreateAttr("iconTextGap", "4")
|
||||
ElementYEdgeLabel.CreateAttr("modelName", "centered")
|
||||
ElementYEdgeLabel.CreateAttr("modelPosition", "head")
|
||||
ElementYEdgeLabel.CreateAttr("preferredPlacement", "anywhere")
|
||||
ElementYEdgeLabel.CreateAttr("ratio", "0.5")
|
||||
ElementYEdgeLabel.CreateAttr("textColor", "#000000")
|
||||
ElementYEdgeLabel.CreateAttr("verticalTextPosition", "bottom")
|
||||
ElementYEdgeLabel.CreateAttr("visible", "true")
|
||||
ElementYEdgeLabel.CreateAttr("width", "41.8")
|
||||
ElementYEdgeLabel.CreateAttr("x", "71.5")
|
||||
ElementYEdgeLabel.CreateAttr("xml:space", "preserve")
|
||||
ElementYEdgeLabel.CreateAttr("y", "0.5")
|
||||
ElementYEdgeLabel.CreateAttr("bottomInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("leftInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("rightInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("topInset", "0")
|
||||
ElementYEdgeLabel.CreateText(label)
|
||||
|
||||
//y:PreferredPlacementDescriptor
|
||||
ElementYPreferredPlacementDescriptor := ElementYEdgeLabel.CreateElement("y:PreferredPlacementDescriptor")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angle", "0.0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleOffsetOnRightSide", "0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleReference", "absolute")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleRotationOnRightSide", "co")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("distance", "-1.0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("frozen", "true")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("placement", "anywhere")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("side", "anywhere")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("sideReference", "relative_to_edge_flow")
|
||||
|
||||
//y:BendStyle
|
||||
ElementYBendStyle := ElementYPolyLineEdge.CreateElement("y:BendStyle")
|
||||
ElementYBendStyle.CreateAttr("smoothed", "false")
|
||||
|
||||
return ElementEdge
|
||||
}
|
||||
|
||||
// CreateElement_Edge_blue - создаёт элемент xgml - стрелка синяя с заголовком
|
||||
func CreateElement_Edge_blue(ElementGraph, ElementFrom, ElementTo *etree.Element, label, Description string) *etree.Element {
|
||||
|
||||
//node
|
||||
ElementEdge := ElementGraph.CreateElement("edge")
|
||||
//EdgeId := FindId(ElementGraph, ElementEdge)
|
||||
//EdgeID := EdgeId
|
||||
EdgeID := "e" + strconv.Itoa(ElementEdge.Index())
|
||||
ElementEdge.CreateAttr("id", EdgeID)
|
||||
//Source := "n" + strconv.Itoa(IndexElementFrom) + "::" + "n" + strconv.Itoa(IndexElementTo)
|
||||
IdFrom := FindId(ElementGraph, ElementFrom)
|
||||
IdTo := FindId(ElementGraph, ElementTo)
|
||||
ElementEdge.CreateAttr("source", IdFrom)
|
||||
ElementEdge.CreateAttr("target", IdTo)
|
||||
|
||||
//data
|
||||
ElementData := ElementEdge.CreateElement("data")
|
||||
ElementData.CreateAttr("key", "d8")
|
||||
ElementData.CreateAttr("xml:space", "preserve")
|
||||
//ElementData.CreateText("<![CDATA[descr]]>")
|
||||
//ElementData.CreateElement("![CDATA[descr]]")
|
||||
ElementData.CreateCData(Description)
|
||||
|
||||
//data2
|
||||
ElementData2 := ElementEdge.CreateElement("data")
|
||||
ElementData2.CreateAttr("key", "d9")
|
||||
|
||||
//y:PolyLineEdge
|
||||
ElementYPolyLineEdge := ElementData2.CreateElement("y:PolyLineEdge")
|
||||
|
||||
//y:Path
|
||||
ElementYPath := ElementYPolyLineEdge.CreateElement("y:Path")
|
||||
ElementYPath.CreateAttr("sx", "0.0")
|
||||
ElementYPath.CreateAttr("sy", "0.0")
|
||||
ElementYPath.CreateAttr("tx", "0.0")
|
||||
ElementYPath.CreateAttr("ty", "0.0")
|
||||
|
||||
//y:LineStyle
|
||||
ElementYLineStyle := ElementYPolyLineEdge.CreateElement("y:LineStyle")
|
||||
ElementYLineStyle.CreateAttr("color", "#0000FF")
|
||||
ElementYLineStyle.CreateAttr("type", "line")
|
||||
ElementYLineStyle.CreateAttr("width", "1.0")
|
||||
|
||||
//y:Arrows
|
||||
ElementYArrows := ElementYPolyLineEdge.CreateElement("y:Arrows")
|
||||
ElementYArrows.CreateAttr("source", "none")
|
||||
ElementYArrows.CreateAttr("target", "standard")
|
||||
|
||||
//y:EdgeLabel
|
||||
ElementYEdgeLabel := ElementYPolyLineEdge.CreateElement("y:EdgeLabel")
|
||||
ElementYEdgeLabel.CreateAttr("alignment", "center")
|
||||
ElementYEdgeLabel.CreateAttr("configuration", "AutoFlippingLabel")
|
||||
ElementYEdgeLabel.CreateAttr("distance", "0.0")
|
||||
ElementYEdgeLabel.CreateAttr("fontFamily", "Dialog")
|
||||
ElementYEdgeLabel.CreateAttr("fontSize", strconv.Itoa(FONT_SIZE_EDGE))
|
||||
ElementYEdgeLabel.CreateAttr("fontStyle", "plain")
|
||||
ElementYEdgeLabel.CreateAttr("hasBackgroundColor", "false")
|
||||
ElementYEdgeLabel.CreateAttr("hasLineColor", "false")
|
||||
ElementYEdgeLabel.CreateAttr("height", "17.96875")
|
||||
ElementYEdgeLabel.CreateAttr("horizontalTextPosition", "center")
|
||||
ElementYEdgeLabel.CreateAttr("iconTextGap", "4")
|
||||
ElementYEdgeLabel.CreateAttr("modelName", "centered")
|
||||
ElementYEdgeLabel.CreateAttr("modelPosition", "head")
|
||||
ElementYEdgeLabel.CreateAttr("preferredPlacement", "anywhere")
|
||||
ElementYEdgeLabel.CreateAttr("ratio", "0.5")
|
||||
ElementYEdgeLabel.CreateAttr("textColor", "#0000FF")
|
||||
ElementYEdgeLabel.CreateAttr("verticalTextPosition", "bottom")
|
||||
ElementYEdgeLabel.CreateAttr("visible", "true")
|
||||
ElementYEdgeLabel.CreateAttr("width", "41.8")
|
||||
ElementYEdgeLabel.CreateAttr("x", "71.5")
|
||||
ElementYEdgeLabel.CreateAttr("xml:space", "preserve")
|
||||
ElementYEdgeLabel.CreateAttr("y", "0.5")
|
||||
ElementYEdgeLabel.CreateAttr("bottomInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("leftInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("rightInset", "0")
|
||||
ElementYEdgeLabel.CreateAttr("topInset", "0")
|
||||
ElementYEdgeLabel.CreateText(label)
|
||||
|
||||
//y:PreferredPlacementDescriptor
|
||||
ElementYPreferredPlacementDescriptor := ElementYEdgeLabel.CreateElement("y:PreferredPlacementDescriptor")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angle", "0.0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleOffsetOnRightSide", "0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleReference", "absolute")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("angleRotationOnRightSide", "co")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("distance", "-1.0")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("frozen", "true")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("placement", "anywhere")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("side", "anywhere")
|
||||
ElementYPreferredPlacementDescriptor.CreateAttr("sideReference", "relative_to_edge_flow")
|
||||
|
||||
//y:BendStyle
|
||||
ElementYBendStyle := ElementYPolyLineEdge.CreateElement("y:BendStyle")
|
||||
ElementYBendStyle.CreateAttr("smoothed", "false")
|
||||
|
||||
return ElementEdge
|
||||
}
|
||||
|
||||
// findWidth_Shape - возвращает число - ширину элемента
|
||||
func findWidth_Shape(ElementName string) int {
|
||||
Otvet := FONT_SIZE_SHAPE * 2
|
||||
|
||||
LenMax := findMaxLenRow(ElementName)
|
||||
var OtvetF float64
|
||||
OtvetF = float64(Otvet) + float64(LenMax)*float64(FONT_SIZE_SHAPE/2)
|
||||
Otvet = int(math.Round(OtvetF))
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// findHeight_Shape - возвращает число - высоту элемента
|
||||
func findHeight_Shape(ElementName string) int {
|
||||
|
||||
Otvet := 10 + FONT_SIZE_SHAPE*3
|
||||
|
||||
RowsTotal := countLines(ElementName)
|
||||
|
||||
Otvet = Otvet + (RowsTotal-1)*FONT_SIZE_SHAPE*2
|
||||
|
||||
return Otvet
|
||||
|
||||
}
|
||||
|
||||
// findWidth_Group - возвращает число - ширину элемента
|
||||
func findWidth_Group(ElementName string) int {
|
||||
Otvet := 10
|
||||
|
||||
LenMax := findMaxLenRow(ElementName)
|
||||
var OtvetF float64
|
||||
OtvetF = float64(Otvet) + float64(LenMax)*10
|
||||
Otvet = int(math.Round(OtvetF))
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// findHeight_Group - возвращает число - высоту элемента
|
||||
func findHeight_Group(ElementName string) int {
|
||||
|
||||
Otvet := 30
|
||||
|
||||
RowsTotal := countLines(ElementName)
|
||||
|
||||
Otvet = Otvet + (RowsTotal-1)*18
|
||||
|
||||
return Otvet
|
||||
|
||||
}
|
||||
|
||||
// findWidth_Edge - возвращает число - ширину элемента
|
||||
func findWidth_Edge(Label string) int {
|
||||
Otvet := 10
|
||||
|
||||
LenMax := findMaxLenRow(Label)
|
||||
var OtvetF float64
|
||||
OtvetF = float64(Otvet) + float64(LenMax)*10
|
||||
Otvet = int(math.Round(OtvetF))
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// findHeight_Edge - возвращает число - высоту элемента
|
||||
func findHeight_Edge(Label string) int {
|
||||
|
||||
Otvet := 30
|
||||
|
||||
RowsTotal := countLines(Label)
|
||||
|
||||
Otvet = Otvet + (RowsTotal-1)*18
|
||||
|
||||
return Otvet
|
||||
|
||||
}
|
||||
|
||||
// countLines - возвращает количество переводов строки
|
||||
func countLines(s string) int {
|
||||
Otvet := 0
|
||||
|
||||
Otvet = strings.Count(s, "\n")
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// findMaxLenRow - возвращает количество символов в строке максимум
|
||||
func findMaxLenRow(ElementName string) int {
|
||||
Otvet := 0
|
||||
|
||||
Mass := strings.Split(ElementName, "\n")
|
||||
|
||||
for _, Mass1 := range Mass {
|
||||
len1 := len(Mass1)
|
||||
if len1 > Otvet {
|
||||
Otvet = len1
|
||||
}
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// CreateDocument - создаёт новый документ .xgml
|
||||
func CreateDocument() (*etree.Document, *etree.Element) {
|
||||
|
||||
DocXML := etree.NewDocument()
|
||||
DocXML.CreateProcInst("xml", `version="1.0" encoding="UTF-8" standalone="no"`)
|
||||
|
||||
ElementGraphMl := DocXML.CreateElement("graphml")
|
||||
ElementGraphMl.CreateAttr("xmlns", "http://graphml.graphdrawing.org/xmlns")
|
||||
ElementGraphMl.CreateAttr("xmlns:java", "http://www.yworks.com/xml/yfiles-common/1.0/java")
|
||||
ElementGraphMl.CreateAttr("xmlns:sys", "http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0")
|
||||
ElementGraphMl.CreateAttr("xmlns:x", "http://www.yworks.com/xml/yfiles-common/markup/2.0")
|
||||
ElementGraphMl.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
ElementGraphMl.CreateAttr("xmlns:y", "http://www.yworks.com/xml/graphml")
|
||||
ElementGraphMl.CreateAttr("xmlns:y", "http://www.yworks.com/xml/graphml")
|
||||
ElementGraphMl.CreateAttr("xmlns:yed", "http://www.yworks.com/xml/yed/3")
|
||||
ElementGraphMl.CreateAttr("xsi:schemaLocation", "http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd")
|
||||
|
||||
ElementD0 := ElementGraphMl.CreateElement("key")
|
||||
ElementD0.CreateAttr("for", "port")
|
||||
ElementD0.CreateAttr("id", "d0")
|
||||
ElementD0.CreateAttr("yfiles.type", "portgraphics")
|
||||
|
||||
ElementD1 := ElementGraphMl.CreateElement("key")
|
||||
ElementD1.CreateAttr("for", "port")
|
||||
ElementD1.CreateAttr("id", "d1")
|
||||
ElementD1.CreateAttr("yfiles.type", "portgeometry")
|
||||
|
||||
ElementD2 := ElementGraphMl.CreateElement("key")
|
||||
ElementD2.CreateAttr("for", "port")
|
||||
ElementD2.CreateAttr("id", "d2")
|
||||
ElementD2.CreateAttr("yfiles.type", "portuserdata")
|
||||
|
||||
ElementD3 := ElementGraphMl.CreateElement("key")
|
||||
ElementD3.CreateAttr("attr.name", "url")
|
||||
ElementD3.CreateAttr("attr.type", "string")
|
||||
ElementD3.CreateAttr("for", "node")
|
||||
ElementD3.CreateAttr("id", "d3")
|
||||
|
||||
ElementD4 := ElementGraphMl.CreateElement("key")
|
||||
ElementD4.CreateAttr("attr.name", "description")
|
||||
ElementD4.CreateAttr("attr.type", "string")
|
||||
ElementD4.CreateAttr("for", "node")
|
||||
ElementD4.CreateAttr("id", "d4")
|
||||
|
||||
ElementD5 := ElementGraphMl.CreateElement("key")
|
||||
ElementD5.CreateAttr("for", "node")
|
||||
ElementD5.CreateAttr("id", "d5")
|
||||
ElementD5.CreateAttr("yfiles.type", "nodegraphics")
|
||||
|
||||
ElementD6 := ElementGraphMl.CreateElement("key")
|
||||
ElementD6.CreateAttr("for", "graphml")
|
||||
ElementD6.CreateAttr("id", "d6")
|
||||
ElementD6.CreateAttr("yfiles.type", "resources")
|
||||
|
||||
ElementD7 := ElementGraphMl.CreateElement("key")
|
||||
ElementD7.CreateAttr("attr.name", "url")
|
||||
ElementD7.CreateAttr("attr.type", "string")
|
||||
ElementD7.CreateAttr("for", "edge")
|
||||
ElementD7.CreateAttr("id", "d7")
|
||||
|
||||
ElementD8 := ElementGraphMl.CreateElement("key")
|
||||
ElementD8.CreateAttr("attr.name", "description")
|
||||
ElementD8.CreateAttr("attr.type", "string")
|
||||
ElementD8.CreateAttr("for", "edge")
|
||||
ElementD8.CreateAttr("id", "d8")
|
||||
|
||||
ElementD9 := ElementGraphMl.CreateElement("key")
|
||||
ElementD9.CreateAttr("for", "edge")
|
||||
ElementD9.CreateAttr("id", "d9")
|
||||
ElementD9.CreateAttr("yfiles.type", "edgegraphics")
|
||||
|
||||
ElementGraph := ElementGraphMl.CreateElement("graph")
|
||||
ElementGraph.CreateAttr("edgedefault", "directed")
|
||||
ElementGraph.CreateAttr("id", "G")
|
||||
|
||||
return DocXML, ElementGraph
|
||||
}
|
||||
|
||||
// FindId - находит ИД в формате "n1::n1::n1"
|
||||
func FindId(ElementGraph0, Element *etree.Element) string {
|
||||
Otvet := ""
|
||||
if Element == nil {
|
||||
return Otvet
|
||||
}
|
||||
|
||||
//if Element == ElementGraph0 {
|
||||
// return Otvet
|
||||
//}
|
||||
|
||||
if Element.Tag == "node" {
|
||||
Otvet = "n" + strconv.Itoa(Element.Index())
|
||||
//return Otvet
|
||||
}
|
||||
|
||||
ParentSID := FindId(ElementGraph0, Element.Parent())
|
||||
if ParentSID != "" {
|
||||
if Otvet == "" {
|
||||
Otvet = ParentSID
|
||||
} else {
|
||||
Otvet = ParentSID + "::" + Otvet
|
||||
}
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
38
pkg/graphml/graphml_test.go
Normal file
38
pkg/graphml/graphml_test.go
Normal file
@ -0,0 +1,38 @@
|
||||
package graphml
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateNewGraphml(t *testing.T) {
|
||||
dir := micro.ProgramDir()
|
||||
DocXML, ElementGraph := CreateDocument()
|
||||
if ElementGraph == nil {
|
||||
|
||||
}
|
||||
|
||||
Shape2 := CreateElement_Shape(ElementGraph, "Shape2")
|
||||
//ElementGraph := DocXML.FindElement("/section/section")
|
||||
//
|
||||
Group1 := CreateElement_Group(ElementGraph, "Group1")
|
||||
//
|
||||
//Element1 := CreateElement_Shape(ElementGraph, Group1, "Entity1")
|
||||
Shape1 := CreateElement_Shape(Group1, "Shape1")
|
||||
CreateElement_Edge(ElementGraph, Shape1, Shape2, "edge1", "descr")
|
||||
CreateElement_Edge_blue(ElementGraph, Shape2, Shape1, "edge2", "descr2")
|
||||
//
|
||||
//CreateElement_Edge_blue(ElementGraph, Element1.Index(), Element2.Index(), "test()")
|
||||
|
||||
if Shape1 == nil || Shape2 == nil {
|
||||
|
||||
}
|
||||
|
||||
FileName := dir + "test" + micro.SeparatorFile() + "test.graphml"
|
||||
//DocXML.IndentTabs()
|
||||
DocXML.Indent(2)
|
||||
err := DocXML.WriteToFile(FileName)
|
||||
if err != nil {
|
||||
t.Error("TestCreateNewXGML() error: ", err)
|
||||
}
|
||||
}
|
32
settings/connections.txt
Normal file
32
settings/connections.txt
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"github.com/segmentio/kafka-go": "Kafka",
|
||||
"gorm.io/driver/postgres": "Postgres",
|
||||
"github.com/camunda/zeebe/clients/go/v8": "Camunda",
|
||||
"gitlab.aescorp.ru/dsp_dev/claim/nikitin/nats_connect": "Nats",
|
||||
"github.com/nats-io/nats.go": "Nats",
|
||||
"github.com/minio/minio-go": "Minio",
|
||||
"github.com/sashabaranov/go-openai": "Chat GPT",
|
||||
"github.com/xhit/go-simple-mail": "EMail server",
|
||||
"github.com/emersion/go-imap": "EMail IMAP server",
|
||||
"github.com/denisenkom/go-mssqldb": "MSSQL",
|
||||
"github.com/lib/pq": "Postgres",
|
||||
"github.com/jackc/pgx": "Postgres",
|
||||
"github.com/gotd": "Telegram",
|
||||
"go.mau.fi/whatsmeow": "Whatsapp",
|
||||
"github.com/mattn/go-sqlite3": "SQL Lite3",
|
||||
"net/http": "WEB",
|
||||
"github.com/valyala/fasthttp": "WEB",
|
||||
"github.com/gin-gonic/gin": "WEB",
|
||||
"github.com/rabbitmq/amqp091-go": "RabbitMQ",
|
||||
"github.com/ManyakRus/starter/kafka_connect": "Kafka",
|
||||
"github.com/ManyakRus/starter/postgres_gorm": "Postgres",
|
||||
"github.com/ManyakRus/starter/camunda_connect": "Camunda",
|
||||
"github.com/ManyakRus/starter/nats_connect": "Nats",
|
||||
"github.com/ManyakRus/starter/minio_connect": "Minio",
|
||||
"github.com/ManyakRus/starter/liveness": "WEB server\nLiveness",
|
||||
"github.com/ManyakRus/starter/whatsapp_connect": "Whatsapp",
|
||||
"github.com/go-redis/redis": "Redis",
|
||||
"github.com/gorilla/websocket": "Web socket",
|
||||
"github.com/Nerzal/gocloak": "Keycloak",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp": "Prometeus"
|
||||
}
|
4
settings/connections_test.txt
Normal file
4
settings/connections_test.txt
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"Service1": "github.com/ManyakRus/1",
|
||||
"Service2": "github.com/ManyakRus/2"
|
||||
}
|
95
test/test.graphml
Normal file
95
test/test.graphml
Normal file
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<key for="port" id="d0" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d1" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d2" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d3"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d4"/>
|
||||
<key for="node" id="d5" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d6" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d7"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d8"/>
|
||||
<key for="edge" id="d9" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<node id="n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="80.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="26.0" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="80.0" x="0.0" xml:space="preserve" y="0.0">Shape2</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="70.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" height="12.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" width="70.0" x="0" xml:space="preserve" y="0">Group1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="12.0" width="70.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasLineColor="false" hasText="false" height="12.0" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" width="70.0" x="0" y="0"/>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="54" bottomF="54.0" left="0" leftF="0.0" right="23" rightF="23.35" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n1:">
|
||||
<node id="n1::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="26.0" width="80.0" x="0.0" y="0.0"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="16" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="26.0" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="80.0" x="0.0" xml:space="preserve" y="0.0">Shape1</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e2" source="n1::n0" target="n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[descr]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:Arrows source="standard" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="centered" modelPosition="head" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="41.8" x="71.5" xml:space="preserve" y="0.5" bottomInset="0" leftInset="0" rightInset="0" topInset="0">edge1
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
<edge id="e3" source="n0" target="n1::n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[descr2]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#0000FF" type="line" width="1.0"/>
|
||||
<y:Arrows source="none" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="0.0" fontFamily="Dialog" fontSize="8" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="centered" modelPosition="head" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="41.8" x="71.5" xml:space="preserve" y="0.5" bottomInset="0" leftInset="0" rightInset="0" topInset="0">edge2
|
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
|
||||
</y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
</graphml>
|
159
test/пустой_graphml.graphml
Normal file
159
test/пустой_graphml.graphml
Normal file
@ -0,0 +1,159 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
|
||||
<!--Created by yEd 3.21.1-->
|
||||
<key for="port" id="d0" yfiles.type="portgraphics"/>
|
||||
<key for="port" id="d1" yfiles.type="portgeometry"/>
|
||||
<key for="port" id="d2" yfiles.type="portuserdata"/>
|
||||
<key attr.name="url" attr.type="string" for="node" id="d3"/>
|
||||
<key attr.name="description" attr.type="string" for="node" id="d4"/>
|
||||
<key for="node" id="d5" yfiles.type="nodegraphics"/>
|
||||
<key for="graphml" id="d6" yfiles.type="resources"/>
|
||||
<key attr.name="url" attr.type="string" for="edge" id="d7"/>
|
||||
<key attr.name="description" attr.type="string" for="edge" id="d8"/>
|
||||
<key for="edge" id="d9" yfiles.type="edgegraphics"/>
|
||||
<graph edgedefault="directed" id="G">
|
||||
<node id="n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="12.0" width="60.0" x="276.0" y="45.0"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="49.2109375" x="5.39453125" xml:space="preserve" y="-2.984375">Shape2</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1" yfiles.foldertype="group">
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="0">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="216.96875" width="415.813327390625" x="-195.269775390625" y="-37.984375"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="59.53955078125" x="178.1368883046875" xml:space="preserve" y="-21.4609375">Group1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="false" closedHeight="80.0" closedWidth="100.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="10" bottomF="10.484375" left="5" leftF="4.769775390625" right="96" rightF="96.35007999999999" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="47.96875" width="167.35008" x="-46.806528" y="10.015625"/>
|
||||
<y:Fill color="#F5F5F5" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="content" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="117.45947265625" x="24.945303671874996" xml:space="preserve" y="-21.4609375">GroupCaption1</y:NodeLabel>
|
||||
<y:Shape type="roundrectangle"/>
|
||||
<y:State closed="true" closedHeight="47.96875" closedWidth="167.35008" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n1:">
|
||||
<node id="n1::n0">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="12.0" width="60.0" x="49.193472" y="-20.0"/>
|
||||
<y:Fill color="#FFFFFF" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="c" textColor="#000000" verticalTextPosition="bottom" visible="true" width="49.2109375" x="5.39453125" xml:space="preserve" y="-2.984375">Shape1</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
<node id="n1::n1" yfiles.foldertype="folder">
|
||||
<data key="d3" xml:space="preserve"/>
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="1">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="-162.5" y="75.5"/>
|
||||
<y:Fill color="#F2F0D8" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#B7B69E" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" xml:space="preserve" y="0.0">3</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
<y:DropShadow color="#D2D2D2" offsetX="4" offsetY="4"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="161.0" width="206.0" x="-175.5" y="-11.5"/>
|
||||
<y:Fill color="#F2F0D8" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#B7B69E" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="206.0" x="0.0" xml:space="preserve" y="0.0">Group2</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
<y:DropShadow color="#D2D2D2" offsetX="4" offsetY="4"/>
|
||||
<y:State closed="true" closedHeight="161.0" closedWidth="206.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n1::n1:">
|
||||
<node id="n1::n1::n0" yfiles.foldertype="folder">
|
||||
<data key="d3" xml:space="preserve"/>
|
||||
<data key="d5">
|
||||
<y:ProxyAutoBoundsNode>
|
||||
<y:Realizers active="1">
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="50.0" width="50.0" x="-71.5" y="83.5"/>
|
||||
<y:Fill color="#F2F0D8" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#B7B69E" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="50.0" x="0.0" xml:space="preserve" y="0.0">3</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
<y:DropShadow color="#D2D2D2" offsetX="4" offsetY="4"/>
|
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
<y:GroupNode>
|
||||
<y:Geometry height="149.0" width="182.0" x="-196.5" y="15.5"/>
|
||||
<y:Fill color="#F2F0D8" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#B7B69E" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="182.0" x="0.0" xml:space="preserve" y="0.0">Group3</y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
<y:DropShadow color="#D2D2D2" offsetX="4" offsetY="4"/>
|
||||
<y:State closed="true" closedHeight="149.0" closedWidth="182.0" innerGraphDisplayEnabled="false"/>
|
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
|
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
|
||||
</y:GroupNode>
|
||||
</y:Realizers>
|
||||
</y:ProxyAutoBoundsNode>
|
||||
</data>
|
||||
<graph edgedefault="directed" id="n1::n1::n0:"/>
|
||||
</node>
|
||||
<node id="n1::n1::n1">
|
||||
<data key="d5">
|
||||
<y:ShapeNode>
|
||||
<y:Geometry height="30.0" width="30.0" x="-87.5" y="55.5"/>
|
||||
<y:Fill color="#FFCC00" transparent="false"/>
|
||||
<y:BorderStyle color="#000000" raised="false" type="line" width="1.0"/>
|
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="49.2109375" x="-9.60546875" xml:space="preserve" y="6.015625">Shape3<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
|
||||
<y:Shape type="rectangle"/>
|
||||
</y:ShapeNode>
|
||||
</data>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
</graph>
|
||||
</node>
|
||||
<edge id="e0" source="n1::n0" target="n0">
|
||||
<data key="d8" xml:space="preserve"><![CDATA[descr]]></data>
|
||||
<data key="d9">
|
||||
<y:PolyLineEdge>
|
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
|
||||
<y:LineStyle color="#0000FF" type="line" width="1.0"/>
|
||||
<y:Arrows source="standard" target="standard"/>
|
||||
<y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="two_pos" modelPosition="head" preferredPlacement="anywhere" ratio="0.5" textColor="#0000FF" verticalTextPosition="bottom" visible="true" width="41.833984375" x="71.55028824609377" xml:space="preserve" y="0.5366937401929626">Edge1<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/></y:EdgeLabel>
|
||||
<y:BendStyle smoothed="false"/>
|
||||
</y:PolyLineEdge>
|
||||
</data>
|
||||
</edge>
|
||||
</graph>
|
||||
<data key="d6">
|
||||
<y:Resources/>
|
||||
</data>
|
||||
</graphml>
|
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
Normal file
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
Normal file
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# filippo.io/edwards25519
|
||||
|
||||
```
|
||||
import "filippo.io/edwards25519"
|
||||
```
|
||||
|
||||
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
|
||||
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
|
||||
|
||||
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255, and was finally [merged back into the Go standard library](https://golang.org/cl/276272) as of Go 1.17. It now tracks the upstream codebase and extends it with additional functionality.
|
||||
|
||||
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
||||
|
||||
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.
|
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
Normal file
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package edwards25519 implements group logic for the twisted Edwards curve
|
||||
//
|
||||
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
|
||||
//
|
||||
// This is better known as the Edwards curve equivalent to Curve25519, and is
|
||||
// the curve used by the Ed25519 signature scheme.
|
||||
//
|
||||
// Most users don't need this package, and should instead use crypto/ed25519 for
|
||||
// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or
|
||||
// github.com/gtank/ristretto255 for prime order group logic.
|
||||
//
|
||||
// However, developers who do need to interact with low-level edwards25519
|
||||
// operations can use this package, which is an extended version of
|
||||
// crypto/ed25519/internal/edwards25519 from the standard library repackaged as
|
||||
// an importable module.
|
||||
package edwards25519
|
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
Normal file
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
Normal file
@ -0,0 +1,428 @@
|
||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package edwards25519
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
// Point types.
|
||||
|
||||
type projP1xP1 struct {
|
||||
X, Y, Z, T field.Element
|
||||
}
|
||||
|
||||
type projP2 struct {
|
||||
X, Y, Z field.Element
|
||||
}
|
||||
|
||||
// Point represents a point on the edwards25519 curve.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is NOT valid, and it may be used only as a receiver.
|
||||
type Point struct {
|
||||
// The point is internally represented in extended coordinates (X, Y, Z, T)
|
||||
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
|
||||
x, y, z, t field.Element
|
||||
|
||||
// Make the type not comparable (i.e. used with == or as a map key), as
|
||||
// equivalent points can be represented by different Go values.
|
||||
_ incomparable
|
||||
}
|
||||
|
||||
type incomparable [0]func()
|
||||
|
||||
func checkInitialized(points ...*Point) {
|
||||
for _, p := range points {
|
||||
if p.x == (field.Element{}) && p.y == (field.Element{}) {
|
||||
panic("edwards25519: use of uninitialized Point")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type projCached struct {
|
||||
YplusX, YminusX, Z, T2d field.Element
|
||||
}
|
||||
|
||||
type affineCached struct {
|
||||
YplusX, YminusX, T2d field.Element
|
||||
}
|
||||
|
||||
// Constructors.
|
||||
|
||||
func (v *projP2) Zero() *projP2 {
|
||||
v.X.Zero()
|
||||
v.Y.One()
|
||||
v.Z.One()
|
||||
return v
|
||||
}
|
||||
|
||||
// identity is the point at infinity.
|
||||
var identity, _ = new(Point).SetBytes([]byte{
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||
|
||||
// NewIdentityPoint returns a new Point set to the identity.
|
||||
func NewIdentityPoint() *Point {
|
||||
return new(Point).Set(identity)
|
||||
}
|
||||
|
||||
// generator is the canonical curve basepoint. See TestGenerator for the
|
||||
// correspondence of this encoding with the values in RFC 8032.
|
||||
var generator, _ = new(Point).SetBytes([]byte{
|
||||
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})
|
||||
|
||||
// NewGeneratorPoint returns a new Point set to the canonical generator.
|
||||
func NewGeneratorPoint() *Point {
|
||||
return new(Point).Set(generator)
|
||||
}
|
||||
|
||||
func (v *projCached) Zero() *projCached {
|
||||
v.YplusX.One()
|
||||
v.YminusX.One()
|
||||
v.Z.One()
|
||||
v.T2d.Zero()
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *affineCached) Zero() *affineCached {
|
||||
v.YplusX.One()
|
||||
v.YminusX.One()
|
||||
v.T2d.Zero()
|
||||
return v
|
||||
}
|
||||
|
||||
// Assignments.
|
||||
|
||||
// Set sets v = u, and returns v.
|
||||
func (v *Point) Set(u *Point) *Point {
|
||||
*v = *u
|
||||
return v
|
||||
}
|
||||
|
||||
// Encoding.
|
||||
|
||||
// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
|
||||
// Section 5.1.2.
|
||||
func (v *Point) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var buf [32]byte
|
||||
return v.bytes(&buf)
|
||||
}
|
||||
|
||||
func (v *Point) bytes(buf *[32]byte) []byte {
|
||||
checkInitialized(v)
|
||||
|
||||
var zInv, x, y field.Element
|
||||
zInv.Invert(&v.z) // zInv = 1 / Z
|
||||
x.Multiply(&v.x, &zInv) // x = X / Z
|
||||
y.Multiply(&v.y, &zInv) // y = Y / Z
|
||||
|
||||
out := copyFieldElement(buf, &y)
|
||||
out[31] |= byte(x.IsNegative() << 7)
|
||||
return out
|
||||
}
|
||||
|
||||
var feOne = new(field.Element).One()
|
||||
|
||||
// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not
|
||||
// represent a valid point on the curve, SetBytes returns nil and an error and
|
||||
// the receiver is unchanged. Otherwise, SetBytes returns v.
|
||||
//
|
||||
// Note that SetBytes accepts all non-canonical encodings of valid points.
|
||||
// That is, it follows decoding rules that match most implementations in
|
||||
// the ecosystem rather than RFC 8032.
|
||||
func (v *Point) SetBytes(x []byte) (*Point, error) {
|
||||
// Specifically, the non-canonical encodings that are accepted are
|
||||
// 1) the ones where the field element is not reduced (see the
|
||||
// (*field.Element).SetBytes docs) and
|
||||
// 2) the ones where the x-coordinate is zero and the sign bit is set.
|
||||
//
|
||||
// This is consistent with crypto/ed25519/internal/edwards25519. Read more
|
||||
// at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
|
||||
// "Canonical A, R" section.
|
||||
|
||||
y, err := new(field.Element).SetBytes(x)
|
||||
if err != nil {
|
||||
return nil, errors.New("edwards25519: invalid point encoding length")
|
||||
}
|
||||
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// x² + dx²y² = x²(dy² + 1) = y² - 1
|
||||
// x² = (y² - 1) / (dy² + 1)
|
||||
|
||||
// u = y² - 1
|
||||
y2 := new(field.Element).Square(y)
|
||||
u := new(field.Element).Subtract(y2, feOne)
|
||||
|
||||
// v = dy² + 1
|
||||
vv := new(field.Element).Multiply(y2, d)
|
||||
vv = vv.Add(vv, feOne)
|
||||
|
||||
// x = +√(u/v)
|
||||
xx, wasSquare := new(field.Element).SqrtRatio(u, vv)
|
||||
if wasSquare == 0 {
|
||||
return nil, errors.New("edwards25519: invalid point encoding")
|
||||
}
|
||||
|
||||
// Select the negative square root if the sign bit is set.
|
||||
xxNeg := new(field.Element).Negate(xx)
|
||||
xx = xx.Select(xxNeg, xx, int(x[31]>>7))
|
||||
|
||||
v.x.Set(xx)
|
||||
v.y.Set(y)
|
||||
v.z.One()
|
||||
v.t.Multiply(xx, y) // xy = T / Z
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func copyFieldElement(buf *[32]byte, v *field.Element) []byte {
|
||||
copy(buf[:], v.Bytes())
|
||||
return buf[:]
|
||||
}
|
||||
|
||||
// Conversions.
|
||||
|
||||
func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 {
|
||||
v.X.Multiply(&p.X, &p.T)
|
||||
v.Y.Multiply(&p.Y, &p.Z)
|
||||
v.Z.Multiply(&p.Z, &p.T)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP2) FromP3(p *Point) *projP2 {
|
||||
v.X.Set(&p.x)
|
||||
v.Y.Set(&p.y)
|
||||
v.Z.Set(&p.z)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Point) fromP1xP1(p *projP1xP1) *Point {
|
||||
v.x.Multiply(&p.X, &p.T)
|
||||
v.y.Multiply(&p.Y, &p.Z)
|
||||
v.z.Multiply(&p.Z, &p.T)
|
||||
v.t.Multiply(&p.X, &p.Y)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *Point) fromP2(p *projP2) *Point {
|
||||
v.x.Multiply(&p.X, &p.Z)
|
||||
v.y.Multiply(&p.Y, &p.Z)
|
||||
v.z.Square(&p.Z)
|
||||
v.t.Multiply(&p.X, &p.Y)
|
||||
return v
|
||||
}
|
||||
|
||||
// d is a constant in the curve equation.
|
||||
var d, _ = new(field.Element).SetBytes([]byte{
|
||||
0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
|
||||
0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
|
||||
0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
|
||||
0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52})
|
||||
var d2 = new(field.Element).Add(d, d)
|
||||
|
||||
func (v *projCached) FromP3(p *Point) *projCached {
|
||||
v.YplusX.Add(&p.y, &p.x)
|
||||
v.YminusX.Subtract(&p.y, &p.x)
|
||||
v.Z.Set(&p.z)
|
||||
v.T2d.Multiply(&p.t, d2)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *affineCached) FromP3(p *Point) *affineCached {
|
||||
v.YplusX.Add(&p.y, &p.x)
|
||||
v.YminusX.Subtract(&p.y, &p.x)
|
||||
v.T2d.Multiply(&p.t, d2)
|
||||
|
||||
var invZ field.Element
|
||||
invZ.Invert(&p.z)
|
||||
v.YplusX.Multiply(&v.YplusX, &invZ)
|
||||
v.YminusX.Multiply(&v.YminusX, &invZ)
|
||||
v.T2d.Multiply(&v.T2d, &invZ)
|
||||
return v
|
||||
}
|
||||
|
||||
// (Re)addition and subtraction.
|
||||
|
||||
// Add sets v = p + q, and returns v.
|
||||
func (v *Point) Add(p, q *Point) *Point {
|
||||
checkInitialized(p, q)
|
||||
qCached := new(projCached).FromP3(q)
|
||||
result := new(projP1xP1).Add(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
// Subtract sets v = p - q, and returns v.
|
||||
func (v *Point) Subtract(p, q *Point) *Point {
|
||||
checkInitialized(p, q)
|
||||
qCached := new(projCached).FromP3(q)
|
||||
result := new(projP1xP1).Sub(p, qCached)
|
||||
return v.fromP1xP1(result)
|
||||
}
|
||||
|
||||
func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YplusX)
|
||||
MM.Multiply(&YminusX, &q.YminusX)
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
ZZ2.Multiply(&p.z, &q.Z)
|
||||
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Add(&ZZ2, &TT2d)
|
||||
v.T.Subtract(&ZZ2, &TT2d)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
ZZ2.Multiply(&p.z, &q.Z)
|
||||
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Subtract(&ZZ2, &TT2d) // flipped sign
|
||||
v.T.Add(&ZZ2, &TT2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YplusX)
|
||||
MM.Multiply(&YminusX, &q.YminusX)
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
|
||||
Z2.Add(&p.z, &p.z)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Add(&Z2, &TT2d)
|
||||
v.T.Subtract(&Z2, &TT2d)
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||
|
||||
YplusX.Add(&p.y, &p.x)
|
||||
YminusX.Subtract(&p.y, &p.x)
|
||||
|
||||
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||
TT2d.Multiply(&p.t, &q.T2d)
|
||||
|
||||
Z2.Add(&p.z, &p.z)
|
||||
|
||||
v.X.Subtract(&PP, &MM)
|
||||
v.Y.Add(&PP, &MM)
|
||||
v.Z.Subtract(&Z2, &TT2d) // flipped sign
|
||||
v.T.Add(&Z2, &TT2d) // flipped sign
|
||||
return v
|
||||
}
|
||||
|
||||
// Doubling.
|
||||
|
||||
func (v *projP1xP1) Double(p *projP2) *projP1xP1 {
|
||||
var XX, YY, ZZ2, XplusYsq field.Element
|
||||
|
||||
XX.Square(&p.X)
|
||||
YY.Square(&p.Y)
|
||||
ZZ2.Square(&p.Z)
|
||||
ZZ2.Add(&ZZ2, &ZZ2)
|
||||
XplusYsq.Add(&p.X, &p.Y)
|
||||
XplusYsq.Square(&XplusYsq)
|
||||
|
||||
v.Y.Add(&YY, &XX)
|
||||
v.Z.Subtract(&YY, &XX)
|
||||
|
||||
v.X.Subtract(&XplusYsq, &v.Y)
|
||||
v.T.Subtract(&ZZ2, &v.Z)
|
||||
return v
|
||||
}
|
||||
|
||||
// Negation.
|
||||
|
||||
// Negate sets v = -p, and returns v.
|
||||
func (v *Point) Negate(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
v.x.Negate(&p.x)
|
||||
v.y.Set(&p.y)
|
||||
v.z.Set(&p.z)
|
||||
v.t.Negate(&p.t)
|
||||
return v
|
||||
}
|
||||
|
||||
// Equal returns 1 if v is equivalent to u, and 0 otherwise.
|
||||
func (v *Point) Equal(u *Point) int {
|
||||
checkInitialized(v, u)
|
||||
|
||||
var t1, t2, t3, t4 field.Element
|
||||
t1.Multiply(&v.x, &u.z)
|
||||
t2.Multiply(&u.x, &v.z)
|
||||
t3.Multiply(&v.y, &u.z)
|
||||
t4.Multiply(&u.y, &v.z)
|
||||
|
||||
return t1.Equal(&t2) & t3.Equal(&t4)
|
||||
}
|
||||
|
||||
// Constant-time operations
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *projCached) Select(a, b *projCached, cond int) *projCached {
|
||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||
v.Z.Select(&a.Z, &b.Z, cond)
|
||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||
func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached {
|
||||
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
func (v *projCached) CondNeg(cond int) *projCached {
|
||||
v.YplusX.Swap(&v.YminusX, cond)
|
||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||
return v
|
||||
}
|
||||
|
||||
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||
func (v *affineCached) CondNeg(cond int) *affineCached {
|
||||
v.YplusX.Swap(&v.YminusX, cond)
|
||||
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||
return v
|
||||
}
|
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
Normal file
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
Normal file
@ -0,0 +1,343 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package edwards25519
|
||||
|
||||
// This file contains additional functionality that is not included in the
|
||||
// upstream crypto/ed25519/internal/edwards25519 package.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap. Don't change the style without making
|
||||
// sure it doesn't increase the inliner cost.
|
||||
var e [4]field.Element
|
||||
X, Y, Z, T = v.extendedCoordinates(&e)
|
||||
return
|
||||
}
|
||||
|
||||
func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) {
|
||||
checkInitialized(v)
|
||||
X = e[0].Set(&v.x)
|
||||
Y = e[1].Set(&v.y)
|
||||
Z = e[2].Set(&v.z)
|
||||
T = e[3].Set(&v.t)
|
||||
return
|
||||
}
|
||||
|
||||
// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where
|
||||
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||
//
|
||||
// If the coordinates are invalid or don't represent a valid point on the curve,
|
||||
// SetExtendedCoordinates returns nil and an error and the receiver is
|
||||
// unchanged. Otherwise, SetExtendedCoordinates returns v.
|
||||
func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) {
|
||||
if !isOnCurve(X, Y, Z, T) {
|
||||
return nil, errors.New("edwards25519: invalid point coordinates")
|
||||
}
|
||||
v.x.Set(X)
|
||||
v.y.Set(Y)
|
||||
v.z.Set(Z)
|
||||
v.t.Set(T)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func isOnCurve(X, Y, Z, T *field.Element) bool {
|
||||
var lhs, rhs field.Element
|
||||
XX := new(field.Element).Square(X)
|
||||
YY := new(field.Element).Square(Y)
|
||||
ZZ := new(field.Element).Square(Z)
|
||||
TT := new(field.Element).Square(T)
|
||||
// -x² + y² = 1 + dx²y²
|
||||
// -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
|
||||
// -X² + Y² = Z² + dT²
|
||||
lhs.Subtract(YY, XX)
|
||||
rhs.Multiply(d, TT).Add(&rhs, ZZ)
|
||||
if lhs.Equal(&rhs) != 1 {
|
||||
return false
|
||||
}
|
||||
// xy = T/Z
|
||||
// XY/Z² = T/Z
|
||||
// XY = TZ
|
||||
lhs.Multiply(X, Y)
|
||||
rhs.Multiply(T, Z)
|
||||
return lhs.Equal(&rhs) == 1
|
||||
}
|
||||
|
||||
// BytesMontgomery converts v to a point on the birationally-equivalent
|
||||
// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
|
||||
// according to RFC 7748.
|
||||
//
|
||||
// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
|
||||
// to the same value. If v is the identity point, BytesMontgomery returns 32
|
||||
// zero bytes, analogously to the X25519 function.
|
||||
func (v *Point) BytesMontgomery() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var buf [32]byte
|
||||
return v.bytesMontgomery(&buf)
|
||||
}
|
||||
|
||||
func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
|
||||
checkInitialized(v)
|
||||
|
||||
// RFC 7748, Section 4.1 provides the bilinear map to calculate the
|
||||
// Montgomery u-coordinate
|
||||
//
|
||||
// u = (1 + y) / (1 - y)
|
||||
//
|
||||
// where y = Y / Z.
|
||||
|
||||
var y, recip, u field.Element
|
||||
|
||||
y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z
|
||||
recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y)
|
||||
u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r
|
||||
|
||||
return copyFieldElement(buf, &u)
|
||||
}
|
||||
|
||||
// MultByCofactor sets v = 8 * p, and returns v.
|
||||
func (v *Point) MultByCofactor(p *Point) *Point {
|
||||
checkInitialized(p)
|
||||
result := projP1xP1{}
|
||||
pp := (&projP2{}).FromP3(p)
|
||||
result.Double(pp)
|
||||
pp.FromP1xP1(&result)
|
||||
result.Double(pp)
|
||||
pp.FromP1xP1(&result)
|
||||
result.Double(pp)
|
||||
return v.fromP1xP1(&result)
|
||||
}
|
||||
|
||||
// Given k > 0, set s = s**(2*i).
|
||||
func (s *Scalar) pow2k(k int) {
|
||||
for i := 0; i < k; i++ {
|
||||
s.Multiply(s, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Invert sets s to the inverse of a nonzero scalar v, and returns s.
|
||||
//
|
||||
// If t is zero, Invert returns zero.
|
||||
func (s *Scalar) Invert(t *Scalar) *Scalar {
|
||||
// Uses a hardcoded sliding window of width 4.
|
||||
var table [8]Scalar
|
||||
var tt Scalar
|
||||
tt.Multiply(t, t)
|
||||
table[0] = *t
|
||||
for i := 0; i < 7; i++ {
|
||||
table[i+1].Multiply(&table[i], &tt)
|
||||
}
|
||||
// Now table = [t**1, t**3, t**7, t**11, t**13, t**15]
|
||||
// so t**k = t[k/2] for odd k
|
||||
|
||||
// To compute the sliding window digits, use the following Sage script:
|
||||
|
||||
// sage: import itertools
|
||||
// sage: def sliding_window(w,k):
|
||||
// ....: digits = []
|
||||
// ....: while k > 0:
|
||||
// ....: if k % 2 == 1:
|
||||
// ....: kmod = k % (2**w)
|
||||
// ....: digits.append(kmod)
|
||||
// ....: k = k - kmod
|
||||
// ....: else:
|
||||
// ....: digits.append(0)
|
||||
// ....: k = k // 2
|
||||
// ....: return digits
|
||||
|
||||
// Now we can compute s roughly as follows:
|
||||
|
||||
// sage: s = 1
|
||||
// sage: for coeff in reversed(sliding_window(4,l-2)):
|
||||
// ....: s = s*s
|
||||
// ....: if coeff > 0 :
|
||||
// ....: s = s*t**coeff
|
||||
|
||||
// This works on one bit at a time, with many runs of zeros.
|
||||
// The digits can be collapsed into [(count, coeff)] as follows:
|
||||
|
||||
// sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
|
||||
|
||||
// Entries of the form (k, 0) turn into pow2k(k)
|
||||
// Entries of the form (1, coeff) turn into a squaring and then a table lookup.
|
||||
// We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
|
||||
|
||||
*s = table[1/2]
|
||||
s.pow2k(127 + 1)
|
||||
s.Multiply(s, &table[1/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[13/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[5/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[1/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(5 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
s.pow2k(9 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[3/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[13/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[7/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[9/2])
|
||||
s.pow2k(3 + 1)
|
||||
s.Multiply(s, &table[15/2])
|
||||
s.pow2k(4 + 1)
|
||||
s.Multiply(s, &table[11/2])
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends only on the lengths of the two slices, which must match.
|
||||
func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||
if len(scalars) != len(points) {
|
||||
panic("edwards25519: called MultiScalarMult with different size inputs")
|
||||
}
|
||||
checkInitialized(points...)
|
||||
|
||||
// Proceed as in the single-base case, but share doublings
|
||||
// between each point in the multiscalar equation.
|
||||
|
||||
// Build lookup tables for each point
|
||||
tables := make([]projLookupTable, len(points))
|
||||
for i := range tables {
|
||||
tables[i].FromP3(points[i])
|
||||
}
|
||||
// Compute signed radix-16 digits for each scalar
|
||||
digits := make([][64]int8, len(scalars))
|
||||
for i := range digits {
|
||||
digits[i] = scalars[i].signedRadix16()
|
||||
}
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
for j := range tables {
|
||||
tables[j].SelectInto(multiple, digits[j][63])
|
||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // update v
|
||||
}
|
||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
for i := 62; i >= 0; i-- {
|
||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||
// Lookup-and-add the appropriate multiple of each input point
|
||||
for j := range tables {
|
||||
tables[j].SelectInto(multiple, digits[j][i])
|
||||
tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // update v
|
||||
}
|
||||
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||
if len(scalars) != len(points) {
|
||||
panic("edwards25519: called VarTimeMultiScalarMult with different size inputs")
|
||||
}
|
||||
checkInitialized(points...)
|
||||
|
||||
// Generalize double-base NAF computation to arbitrary sizes.
|
||||
// Here all the points are dynamic, so we only use the smaller
|
||||
// tables.
|
||||
|
||||
// Build lookup tables for each point
|
||||
tables := make([]nafLookupTable5, len(points))
|
||||
for i := range tables {
|
||||
tables[i].FromP3(points[i])
|
||||
}
|
||||
// Compute a NAF for each scalar
|
||||
nafs := make([][256]int8, len(scalars))
|
||||
for i := range nafs {
|
||||
nafs[i] = scalars[i].nonAdjacentForm(5)
|
||||
}
|
||||
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
tmp2.Zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
//
|
||||
// Skip trying to find the first nonzero coefficent, because
|
||||
// searching might be more work than a few extra doublings.
|
||||
for i := 255; i >= 0; i-- {
|
||||
tmp1.Double(tmp2)
|
||||
|
||||
for j := range nafs {
|
||||
if nafs[j][i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
tables[j].SelectInto(multiple, nafs[j][i])
|
||||
tmp1.Add(v, multiple)
|
||||
} else if nafs[j][i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
tables[j].SelectInto(multiple, -nafs[j][i])
|
||||
tmp1.Sub(v, multiple)
|
||||
}
|
||||
}
|
||||
|
||||
tmp2.FromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
v.fromP2(tmp2)
|
||||
return v
|
||||
}
|
420
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
Normal file
420
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
Normal file
@ -0,0 +1,420 @@
|
||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package field implements fast arithmetic modulo 2^255-19.
|
||||
package field
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Element represents an element of the field GF(2^255-19). Note that this
|
||||
// is not a cryptographically secure group, and should only be used to interact
|
||||
// with edwards25519.Point coordinates.
|
||||
//
|
||||
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||
// are allowed to alias.
|
||||
//
|
||||
// The zero value is a valid zero element.
|
||||
type Element struct {
|
||||
// An element t represents the integer
|
||||
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||
//
|
||||
// Between operations, all limbs are expected to be lower than 2^52.
|
||||
l0 uint64
|
||||
l1 uint64
|
||||
l2 uint64
|
||||
l3 uint64
|
||||
l4 uint64
|
||||
}
|
||||
|
||||
const maskLow51Bits uint64 = (1 << 51) - 1
|
||||
|
||||
var feZero = &Element{0, 0, 0, 0, 0}
|
||||
|
||||
// Zero sets v = 0, and returns v.
|
||||
func (v *Element) Zero() *Element {
|
||||
*v = *feZero
|
||||
return v
|
||||
}
|
||||
|
||||
var feOne = &Element{1, 0, 0, 0, 0}
|
||||
|
||||
// One sets v = 1, and returns v.
|
||||
func (v *Element) One() *Element {
|
||||
*v = *feOne
|
||||
return v
|
||||
}
|
||||
|
||||
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||
func (v *Element) reduce() *Element {
|
||||
v.carryPropagate()
|
||||
|
||||
// After the light reduction we now have a field element representation
|
||||
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||
|
||||
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||
c := (v.l0 + 19) >> 51
|
||||
c = (v.l1 + c) >> 51
|
||||
c = (v.l2 + c) >> 51
|
||||
c = (v.l3 + c) >> 51
|
||||
c = (v.l4 + c) >> 51
|
||||
|
||||
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||
// effectively applying the reduction identity to the carry.
|
||||
v.l0 += 19 * c
|
||||
|
||||
v.l1 += v.l0 >> 51
|
||||
v.l0 = v.l0 & maskLow51Bits
|
||||
v.l2 += v.l1 >> 51
|
||||
v.l1 = v.l1 & maskLow51Bits
|
||||
v.l3 += v.l2 >> 51
|
||||
v.l2 = v.l2 & maskLow51Bits
|
||||
v.l4 += v.l3 >> 51
|
||||
v.l3 = v.l3 & maskLow51Bits
|
||||
// no additional carry
|
||||
v.l4 = v.l4 & maskLow51Bits
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// Add sets v = a + b, and returns v.
|
||||
func (v *Element) Add(a, b *Element) *Element {
|
||||
v.l0 = a.l0 + b.l0
|
||||
v.l1 = a.l1 + b.l1
|
||||
v.l2 = a.l2 + b.l2
|
||||
v.l3 = a.l3 + b.l3
|
||||
v.l4 = a.l4 + b.l4
|
||||
// Using the generic implementation here is actually faster than the
|
||||
// assembly. Probably because the body of this function is so simple that
|
||||
// the compiler can figure out better optimizations by inlining the carry
|
||||
// propagation.
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
||||
|
||||
// Subtract sets v = a - b, and returns v.
|
||||
func (v *Element) Subtract(a, b *Element) *Element {
|
||||
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||
return v.carryPropagate()
|
||||
}
|
||||
|
||||
// Negate sets v = -a, and returns v.
|
||||
func (v *Element) Negate(a *Element) *Element {
|
||||
return v.Subtract(feZero, a)
|
||||
}
|
||||
|
||||
// Invert sets v = 1/z mod p, and returns v.
|
||||
//
|
||||
// If z == 0, Invert returns v = 0.
|
||||
func (v *Element) Invert(z *Element) *Element {
|
||||
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
||||
|
||||
z2.Square(z) // 2
|
||||
t.Square(&z2) // 4
|
||||
t.Square(&t) // 8
|
||||
z9.Multiply(&t, z) // 9
|
||||
z11.Multiply(&z9, &z2) // 11
|
||||
t.Square(&z11) // 22
|
||||
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
||||
|
||||
t.Square(&z2_5_0) // 2^6 - 2^1
|
||||
for i := 0; i < 4; i++ {
|
||||
t.Square(&t) // 2^10 - 2^5
|
||||
}
|
||||
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
||||
|
||||
t.Square(&z2_10_0) // 2^11 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^20 - 2^10
|
||||
}
|
||||
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
||||
|
||||
t.Square(&z2_20_0) // 2^21 - 2^1
|
||||
for i := 0; i < 19; i++ {
|
||||
t.Square(&t) // 2^40 - 2^20
|
||||
}
|
||||
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
||||
|
||||
t.Square(&t) // 2^41 - 2^1
|
||||
for i := 0; i < 9; i++ {
|
||||
t.Square(&t) // 2^50 - 2^10
|
||||
}
|
||||
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
||||
|
||||
t.Square(&z2_50_0) // 2^51 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^100 - 2^50
|
||||
}
|
||||
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
||||
|
||||
t.Square(&z2_100_0) // 2^101 - 2^1
|
||||
for i := 0; i < 99; i++ {
|
||||
t.Square(&t) // 2^200 - 2^100
|
||||
}
|
||||
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
||||
|
||||
t.Square(&t) // 2^201 - 2^1
|
||||
for i := 0; i < 49; i++ {
|
||||
t.Square(&t) // 2^250 - 2^50
|
||||
}
|
||||
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
||||
|
||||
t.Square(&t) // 2^251 - 2^1
|
||||
t.Square(&t) // 2^252 - 2^2
|
||||
t.Square(&t) // 2^253 - 2^3
|
||||
t.Square(&t) // 2^254 - 2^4
|
||||
t.Square(&t) // 2^255 - 2^5
|
||||
|
||||
return v.Multiply(&t, &z11) // 2^255 - 21
|
||||
}
|
||||
|
||||
// Set sets v = a, and returns v.
|
||||
func (v *Element) Set(a *Element) *Element {
|
||||
*v = *a
|
||||
return v
|
||||
}
|
||||
|
||||
// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is
|
||||
// not of the right length, SetBytes returns nil and an error, and the
|
||||
// receiver is unchanged.
|
||||
//
|
||||
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||
// are accepted. Note that this is laxer than specified by RFC 8032, but
|
||||
// consistent with most Ed25519 implementations.
|
||||
func (v *Element) SetBytes(x []byte) (*Element, error) {
|
||||
if len(x) != 32 {
|
||||
return nil, errors.New("edwards25519: invalid field element input size")
|
||||
}
|
||||
|
||||
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
||||
v.l0 &= maskLow51Bits
|
||||
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
||||
v.l1 &= maskLow51Bits
|
||||
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
||||
v.l2 &= maskLow51Bits
|
||||
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
||||
v.l3 &= maskLow51Bits
|
||||
// Bits 204:255 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
||||
v.l4 &= maskLow51Bits
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
||||
func (v *Element) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [32]byte
|
||||
return v.bytes(&out)
|
||||
}
|
||||
|
||||
func (v *Element) bytes(out *[32]byte) []byte {
|
||||
t := *v
|
||||
t.reduce()
|
||||
|
||||
var buf [8]byte
|
||||
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
||||
bitsOffset := i * 51
|
||||
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
||||
for i, bb := range buf {
|
||||
off := bitsOffset/8 + i
|
||||
if off >= len(out) {
|
||||
break
|
||||
}
|
||||
out[off] |= bb
|
||||
}
|
||||
}
|
||||
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
||||
func (v *Element) Equal(u *Element) int {
|
||||
sa, sv := u.Bytes(), v.Bytes()
|
||||
return subtle.ConstantTimeCompare(sa, sv)
|
||||
}
|
||||
|
||||
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
||||
|
||||
// Select sets v to a if cond == 1, and to b if cond == 0.
|
||||
func (v *Element) Select(a, b *Element, cond int) *Element {
|
||||
m := mask64Bits(cond)
|
||||
v.l0 = (m & a.l0) | (^m & b.l0)
|
||||
v.l1 = (m & a.l1) | (^m & b.l1)
|
||||
v.l2 = (m & a.l2) | (^m & b.l2)
|
||||
v.l3 = (m & a.l3) | (^m & b.l3)
|
||||
v.l4 = (m & a.l4) | (^m & b.l4)
|
||||
return v
|
||||
}
|
||||
|
||||
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||
func (v *Element) Swap(u *Element, cond int) {
|
||||
m := mask64Bits(cond)
|
||||
t := m & (v.l0 ^ u.l0)
|
||||
v.l0 ^= t
|
||||
u.l0 ^= t
|
||||
t = m & (v.l1 ^ u.l1)
|
||||
v.l1 ^= t
|
||||
u.l1 ^= t
|
||||
t = m & (v.l2 ^ u.l2)
|
||||
v.l2 ^= t
|
||||
u.l2 ^= t
|
||||
t = m & (v.l3 ^ u.l3)
|
||||
v.l3 ^= t
|
||||
u.l3 ^= t
|
||||
t = m & (v.l4 ^ u.l4)
|
||||
v.l4 ^= t
|
||||
u.l4 ^= t
|
||||
}
|
||||
|
||||
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
||||
func (v *Element) IsNegative() int {
|
||||
return int(v.Bytes()[0] & 1)
|
||||
}
|
||||
|
||||
// Absolute sets v to |u|, and returns v.
|
||||
func (v *Element) Absolute(u *Element) *Element {
|
||||
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
||||
}
|
||||
|
||||
// Multiply sets v = x * y, and returns v.
|
||||
func (v *Element) Multiply(x, y *Element) *Element {
|
||||
feMul(v, x, y)
|
||||
return v
|
||||
}
|
||||
|
||||
// Square sets v = x * x, and returns v.
|
||||
func (v *Element) Square(x *Element) *Element {
|
||||
feSquare(v, x)
|
||||
return v
|
||||
}
|
||||
|
||||
// Mult32 sets v = x * y, and returns v.
|
||||
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
||||
x0lo, x0hi := mul51(x.l0, y)
|
||||
x1lo, x1hi := mul51(x.l1, y)
|
||||
x2lo, x2hi := mul51(x.l2, y)
|
||||
x3lo, x3hi := mul51(x.l3, y)
|
||||
x4lo, x4hi := mul51(x.l4, y)
|
||||
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
||||
v.l1 = x1lo + x0hi
|
||||
v.l2 = x2lo + x1hi
|
||||
v.l3 = x3lo + x2hi
|
||||
v.l4 = x4lo + x3hi
|
||||
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||
// so we can skip the carry propagation.
|
||||
return v
|
||||
}
|
||||
|
||||
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
||||
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
||||
mh, ml := bits.Mul64(a, uint64(b))
|
||||
lo = ml & maskLow51Bits
|
||||
hi = (mh << 13) | (ml >> 51)
|
||||
return
|
||||
}
|
||||
|
||||
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||
func (v *Element) Pow22523(x *Element) *Element {
|
||||
var t0, t1, t2 Element
|
||||
|
||||
t0.Square(x) // x^2
|
||||
t1.Square(&t0) // x^4
|
||||
t1.Square(&t1) // x^8
|
||||
t1.Multiply(x, &t1) // x^9
|
||||
t0.Multiply(&t0, &t1) // x^11
|
||||
t0.Square(&t0) // x^22
|
||||
t0.Multiply(&t1, &t0) // x^31
|
||||
t1.Square(&t0) // x^62
|
||||
for i := 1; i < 5; i++ { // x^992
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
||||
t1.Square(&t0) // 2^11 - 2
|
||||
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^20 - 1
|
||||
t2.Square(&t1) // 2^21 - 2
|
||||
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^40 - 1
|
||||
t1.Square(&t1) // 2^41 - 2
|
||||
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^50 - 1
|
||||
t1.Square(&t0) // 2^51 - 2
|
||||
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t1.Multiply(&t1, &t0) // 2^100 - 1
|
||||
t2.Square(&t1) // 2^101 - 2
|
||||
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||
t2.Square(&t2)
|
||||
}
|
||||
t1.Multiply(&t2, &t1) // 2^200 - 1
|
||||
t1.Square(&t1) // 2^201 - 2
|
||||
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||
t1.Square(&t1)
|
||||
}
|
||||
t0.Multiply(&t1, &t0) // 2^250 - 1
|
||||
t0.Square(&t0) // 2^251 - 2
|
||||
t0.Square(&t0) // 2^252 - 4
|
||||
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||
}
|
||||
|
||||
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
||||
2233514472574048, 2117202627021982, 765476049583133}
|
||||
|
||||
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
||||
//
|
||||
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
||||
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||
// and returns r and 0.
|
||||
func (r *Element) SqrtRatio(u, v *Element) (R *Element, wasSquare int) {
|
||||
t0 := new(Element)
|
||||
|
||||
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||
v2 := new(Element).Square(v)
|
||||
uv3 := new(Element).Multiply(u, t0.Multiply(v2, v))
|
||||
uv7 := new(Element).Multiply(uv3, t0.Square(v2))
|
||||
rr := new(Element).Multiply(uv3, t0.Pow22523(uv7))
|
||||
|
||||
check := new(Element).Multiply(v, t0.Square(rr)) // check = v * r^2
|
||||
|
||||
uNeg := new(Element).Negate(u)
|
||||
correctSignSqrt := check.Equal(u)
|
||||
flippedSignSqrt := check.Equal(uNeg)
|
||||
flippedSignSqrtI := check.Equal(t0.Multiply(uNeg, sqrtM1))
|
||||
|
||||
rPrime := new(Element).Multiply(rr, sqrtM1) // r_prime = SQRT_M1 * r
|
||||
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||
rr.Select(rPrime, rr, flippedSignSqrt|flippedSignSqrtI)
|
||||
|
||||
r.Absolute(rr) // Choose the nonnegative square root.
|
||||
return r, correctSignSqrt | flippedSignSqrt
|
||||
}
|
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
Normal file
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
// feMul sets out = a * b. It works like feMulGeneric.
|
||||
//go:noescape
|
||||
func feMul(out *Element, a *Element, b *Element)
|
||||
|
||||
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||
//go:noescape
|
||||
func feSquare(out *Element, a *Element)
|
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
Normal file
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
Normal file
@ -0,0 +1,378 @@
|
||||
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||
|
||||
// +build amd64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func feMul(out *Element, a *Element, b *Element)
|
||||
TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), BX
|
||||
|
||||
// r0 = a0×b0
|
||||
MOVQ (CX), AX
|
||||
MULQ (BX)
|
||||
MOVQ AX, DI
|
||||
MOVQ DX, SI
|
||||
|
||||
// r0 += 19×a1×b4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a2×b3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a3×b2
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r0 += 19×a4×b1
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, DI
|
||||
ADCQ DX, SI
|
||||
|
||||
// r1 = a0×b1
|
||||
MOVQ (CX), AX
|
||||
MULQ 8(BX)
|
||||
MOVQ AX, R9
|
||||
MOVQ DX, R8
|
||||
|
||||
// r1 += a1×b0
|
||||
MOVQ 8(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a2×b4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a3×b3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r1 += 19×a4×b2
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R9
|
||||
ADCQ DX, R8
|
||||
|
||||
// r2 = a0×b2
|
||||
MOVQ (CX), AX
|
||||
MULQ 16(BX)
|
||||
MOVQ AX, R11
|
||||
MOVQ DX, R10
|
||||
|
||||
// r2 += a1×b1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += a2×b0
|
||||
MOVQ 16(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a3×b4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r2 += 19×a4×b3
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R11
|
||||
ADCQ DX, R10
|
||||
|
||||
// r3 = a0×b3
|
||||
MOVQ (CX), AX
|
||||
MULQ 24(BX)
|
||||
MOVQ AX, R13
|
||||
MOVQ DX, R12
|
||||
|
||||
// r3 += a1×b2
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a2×b1
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += a3×b0
|
||||
MOVQ 24(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r3 += 19×a4×b4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(BX)
|
||||
ADDQ AX, R13
|
||||
ADCQ DX, R12
|
||||
|
||||
// r4 = a0×b4
|
||||
MOVQ (CX), AX
|
||||
MULQ 32(BX)
|
||||
MOVQ AX, R15
|
||||
MOVQ DX, R14
|
||||
|
||||
// r4 += a1×b3
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 24(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a2×b2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a3×b1
|
||||
MOVQ 24(CX), AX
|
||||
MULQ 8(BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// r4 += a4×b0
|
||||
MOVQ 32(CX), AX
|
||||
MULQ (BX)
|
||||
ADDQ AX, R15
|
||||
ADCQ DX, R14
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, DI, SI
|
||||
SHLQ $0x0d, R9, R8
|
||||
SHLQ $0x0d, R11, R10
|
||||
SHLQ $0x0d, R13, R12
|
||||
SHLQ $0x0d, R15, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ DI, SI
|
||||
SHRQ $0x33, SI
|
||||
MOVQ R9, R8
|
||||
SHRQ $0x33, R8
|
||||
MOVQ R11, R10
|
||||
SHRQ $0x33, R10
|
||||
MOVQ R13, R12
|
||||
SHRQ $0x33, R12
|
||||
MOVQ R15, R14
|
||||
SHRQ $0x33, R14
|
||||
ANDQ AX, DI
|
||||
IMUL3Q $0x13, R14, R14
|
||||
ADDQ R14, DI
|
||||
ANDQ AX, R9
|
||||
ADDQ SI, R9
|
||||
ANDQ AX, R11
|
||||
ADDQ R8, R11
|
||||
ANDQ AX, R13
|
||||
ADDQ R10, R13
|
||||
ANDQ AX, R15
|
||||
ADDQ R12, R15
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ DI, (AX)
|
||||
MOVQ R9, 8(AX)
|
||||
MOVQ R11, 16(AX)
|
||||
MOVQ R13, 24(AX)
|
||||
MOVQ R15, 32(AX)
|
||||
RET
|
||||
|
||||
// func feSquare(out *Element, a *Element)
|
||||
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||
MOVQ a+8(FP), CX
|
||||
|
||||
// r0 = l0×l0
|
||||
MOVQ (CX), AX
|
||||
MULQ (CX)
|
||||
MOVQ AX, SI
|
||||
MOVQ DX, BX
|
||||
|
||||
// r0 += 38×l1×l4
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r0 += 38×l2×l3
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, SI
|
||||
ADCQ DX, BX
|
||||
|
||||
// r1 = 2×l0×l1
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 8(CX)
|
||||
MOVQ AX, R8
|
||||
MOVQ DX, DI
|
||||
|
||||
// r1 += 38×l2×l4
|
||||
MOVQ 16(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r1 += 19×l3×l3
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R8
|
||||
ADCQ DX, DI
|
||||
|
||||
// r2 = 2×l0×l2
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 16(CX)
|
||||
MOVQ AX, R10
|
||||
MOVQ DX, R9
|
||||
|
||||
// r2 += l1×l1
|
||||
MOVQ 8(CX), AX
|
||||
MULQ 8(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r2 += 38×l3×l4
|
||||
MOVQ 24(CX), AX
|
||||
IMUL3Q $0x26, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R10
|
||||
ADCQ DX, R9
|
||||
|
||||
// r3 = 2×l0×l3
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 24(CX)
|
||||
MOVQ AX, R12
|
||||
MOVQ DX, R11
|
||||
|
||||
// r3 += 2×l1×l2
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r3 += 19×l4×l4
|
||||
MOVQ 32(CX), AX
|
||||
IMUL3Q $0x13, AX, AX
|
||||
MULQ 32(CX)
|
||||
ADDQ AX, R12
|
||||
ADCQ DX, R11
|
||||
|
||||
// r4 = 2×l0×l4
|
||||
MOVQ (CX), AX
|
||||
SHLQ $0x01, AX
|
||||
MULQ 32(CX)
|
||||
MOVQ AX, R14
|
||||
MOVQ DX, R13
|
||||
|
||||
// r4 += 2×l1×l3
|
||||
MOVQ 8(CX), AX
|
||||
IMUL3Q $0x02, AX, AX
|
||||
MULQ 24(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// r4 += l2×l2
|
||||
MOVQ 16(CX), AX
|
||||
MULQ 16(CX)
|
||||
ADDQ AX, R14
|
||||
ADCQ DX, R13
|
||||
|
||||
// First reduction chain
|
||||
MOVQ $0x0007ffffffffffff, AX
|
||||
SHLQ $0x0d, SI, BX
|
||||
SHLQ $0x0d, R8, DI
|
||||
SHLQ $0x0d, R10, R9
|
||||
SHLQ $0x0d, R12, R11
|
||||
SHLQ $0x0d, R14, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Second reduction chain (carryPropagate)
|
||||
MOVQ SI, BX
|
||||
SHRQ $0x33, BX
|
||||
MOVQ R8, DI
|
||||
SHRQ $0x33, DI
|
||||
MOVQ R10, R9
|
||||
SHRQ $0x33, R9
|
||||
MOVQ R12, R11
|
||||
SHRQ $0x33, R11
|
||||
MOVQ R14, R13
|
||||
SHRQ $0x33, R13
|
||||
ANDQ AX, SI
|
||||
IMUL3Q $0x13, R13, R13
|
||||
ADDQ R13, SI
|
||||
ANDQ AX, R8
|
||||
ADDQ BX, R8
|
||||
ANDQ AX, R10
|
||||
ADDQ DI, R10
|
||||
ANDQ AX, R12
|
||||
ADDQ R9, R12
|
||||
ANDQ AX, R14
|
||||
ADDQ R11, R14
|
||||
|
||||
// Store output
|
||||
MOVQ out+0(FP), AX
|
||||
MOVQ SI, (AX)
|
||||
MOVQ R8, 8(AX)
|
||||
MOVQ R10, 16(AX)
|
||||
MOVQ R12, 24(AX)
|
||||
MOVQ R14, 32(AX)
|
||||
RET
|
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
Normal file
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || !gc || purego
|
||||
// +build !amd64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||
|
||||
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
Normal file
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build arm64 && gc && !purego
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
package field
|
||||
|
||||
//go:noescape
|
||||
func carryPropagate(v *Element)
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
carryPropagate(v)
|
||||
return v
|
||||
}
|
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
Normal file
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64,gc,!purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
||||
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
||||
// avoids loading R0-R4 twice and uses LDP and STP.
|
||||
//
|
||||
// See https://golang.org/issues/43145 for the main compiler issue.
|
||||
//
|
||||
// func carryPropagate(v *Element)
|
||||
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
||||
MOVD v+0(FP), R20
|
||||
|
||||
LDP 0(R20), (R0, R1)
|
||||
LDP 16(R20), (R2, R3)
|
||||
MOVD 32(R20), R4
|
||||
|
||||
AND $0x7ffffffffffff, R0, R10
|
||||
AND $0x7ffffffffffff, R1, R11
|
||||
AND $0x7ffffffffffff, R2, R12
|
||||
AND $0x7ffffffffffff, R3, R13
|
||||
AND $0x7ffffffffffff, R4, R14
|
||||
|
||||
ADD R0>>51, R11, R11
|
||||
ADD R1>>51, R12, R12
|
||||
ADD R2>>51, R13, R13
|
||||
ADD R3>>51, R14, R14
|
||||
// R4>>51 * 19 + R10 -> R10
|
||||
LSR $51, R4, R21
|
||||
MOVD $19, R22
|
||||
MADD R22, R10, R21, R10
|
||||
|
||||
STP (R10, R11), 0(R20)
|
||||
STP (R12, R13), 16(R20)
|
||||
MOVD R14, 32(R20)
|
||||
|
||||
RET
|
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
Normal file
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !arm64 || !gc || purego
|
||||
// +build !arm64 !gc purego
|
||||
|
||||
package field
|
||||
|
||||
func (v *Element) carryPropagate() *Element {
|
||||
return v.carryPropagateGeneric()
|
||||
}
|
50
vendor/filippo.io/edwards25519/field/fe_extra.go
generated
vendored
Normal file
50
vendor/filippo.io/edwards25519/field/fe_extra.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package field
|
||||
|
||||
import "errors"
|
||||
|
||||
// This file contains additional functionality that is not included in the
|
||||
// upstream crypto/ed25519/internal/edwards25519/field package.
|
||||
|
||||
// SetWideBytes sets v to x, where x is a 64-byte little-endian encoding, which
|
||||
// is reduced modulo the field order. If x is not of the right length,
|
||||
// SetWideBytes returns nil and an error, and the receiver is unchanged.
|
||||
//
|
||||
// SetWideBytes is not necessary to select a uniformly distributed value, and is
|
||||
// only provided for compatibility: SetBytes can be used instead as the chance
|
||||
// of bias is less than 2⁻²⁵⁰.
|
||||
func (v *Element) SetWideBytes(x []byte) (*Element, error) {
|
||||
if len(x) != 64 {
|
||||
return nil, errors.New("edwards25519: invalid SetWideBytes input size")
|
||||
}
|
||||
|
||||
// Split the 64 bytes into two elements, and extract the most significant
|
||||
// bit of each, which is ignored by SetBytes.
|
||||
lo, _ := new(Element).SetBytes(x[:32])
|
||||
loMSB := uint64(x[31] >> 7)
|
||||
hi, _ := new(Element).SetBytes(x[32:])
|
||||
hiMSB := uint64(x[63] >> 7)
|
||||
|
||||
// The output we want is
|
||||
//
|
||||
// v = lo + loMSB * 2²⁵⁵ + hi * 2²⁵⁶ + hiMSB * 2⁵¹¹
|
||||
//
|
||||
// which applying the reduction identity comes out to
|
||||
//
|
||||
// v = lo + loMSB * 19 + hi * 2 * 19 + hiMSB * 2 * 19²
|
||||
//
|
||||
// l0 will be the sum of a 52 bits value (lo.l0), plus a 5 bits value
|
||||
// (loMSB * 19), a 6 bits value (hi.l0 * 2 * 19), and a 10 bits value
|
||||
// (hiMSB * 2 * 19²), so it fits in a uint64.
|
||||
|
||||
v.l0 = lo.l0 + loMSB*19 + hi.l0*2*19 + hiMSB*2*19*19
|
||||
v.l1 = lo.l1 + hi.l1*2*19
|
||||
v.l2 = lo.l2 + hi.l2*2*19
|
||||
v.l3 = lo.l3 + hi.l3*2*19
|
||||
v.l4 = lo.l4 + hi.l4*2*19
|
||||
|
||||
return v.carryPropagate(), nil
|
||||
}
|
266
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
Normal file
266
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package field
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||
// bits.Mul64 and bits.Add64 intrinsics.
|
||||
type uint128 struct {
|
||||
lo, hi uint64
|
||||
}
|
||||
|
||||
// mul64 returns a * b.
|
||||
func mul64(a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// addMul64 returns v + a * b.
|
||||
func addMul64(v uint128, a, b uint64) uint128 {
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
lo, c := bits.Add64(lo, v.lo, 0)
|
||||
hi, _ = bits.Add64(hi, v.hi, c)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||
func shiftRightBy51(a uint128) uint64 {
|
||||
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||
}
|
||||
|
||||
func feMulGeneric(v, a, b *Element) {
|
||||
a0 := a.l0
|
||||
a1 := a.l1
|
||||
a2 := a.l2
|
||||
a3 := a.l3
|
||||
a4 := a.l4
|
||||
|
||||
b0 := b.l0
|
||||
b1 := b.l1
|
||||
b2 := b.l2
|
||||
b3 := b.l3
|
||||
b4 := b.l4
|
||||
|
||||
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||
// with 51-bit limbs instead of digits.
|
||||
//
|
||||
// a4 a3 a2 a1 a0 x
|
||||
// b4 b3 b2 b1 b0 =
|
||||
// ------------------------
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||
//
|
||||
// Reduction can be carried out simultaneously to multiplication. For
|
||||
// example, we do not compute r5: whenever the result of a multiplication
|
||||
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||
//
|
||||
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// Finally we add up the columns into wide, overlapping limbs.
|
||||
|
||||
a1_19 := a1 * 19
|
||||
a2_19 := a2 * 19
|
||||
a3_19 := a3 * 19
|
||||
a4_19 := a4 * 19
|
||||
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
r0 := mul64(a0, b0)
|
||||
r0 = addMul64(r0, a1_19, b4)
|
||||
r0 = addMul64(r0, a2_19, b3)
|
||||
r0 = addMul64(r0, a3_19, b2)
|
||||
r0 = addMul64(r0, a4_19, b1)
|
||||
|
||||
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||
r1 := mul64(a0, b1)
|
||||
r1 = addMul64(r1, a1, b0)
|
||||
r1 = addMul64(r1, a2_19, b4)
|
||||
r1 = addMul64(r1, a3_19, b3)
|
||||
r1 = addMul64(r1, a4_19, b2)
|
||||
|
||||
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||
r2 := mul64(a0, b2)
|
||||
r2 = addMul64(r2, a1, b1)
|
||||
r2 = addMul64(r2, a2, b0)
|
||||
r2 = addMul64(r2, a3_19, b4)
|
||||
r2 = addMul64(r2, a4_19, b3)
|
||||
|
||||
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||
r3 := mul64(a0, b3)
|
||||
r3 = addMul64(r3, a1, b2)
|
||||
r3 = addMul64(r3, a2, b1)
|
||||
r3 = addMul64(r3, a3, b0)
|
||||
r3 = addMul64(r3, a4_19, b4)
|
||||
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
r4 := mul64(a0, b4)
|
||||
r4 = addMul64(r4, a1, b3)
|
||||
r4 = addMul64(r4, a2, b2)
|
||||
r4 = addMul64(r4, a3, b1)
|
||||
r4 = addMul64(r4, a4, b0)
|
||||
|
||||
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||
// to respect the Element invariant.
|
||||
//
|
||||
// Overall, the reduction works the same as carryPropagate, except with
|
||||
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||
// according to the reduction identity and added to the lowest limb.
|
||||
//
|
||||
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
||||
//
|
||||
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||
// r0 < 2¹¹¹
|
||||
//
|
||||
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
||||
// allows us to easily apply the reduction identity.
|
||||
//
|
||||
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||
// r4 < 5 × 2⁵² × 2⁵²
|
||||
// r4 < 2¹⁰⁷
|
||||
//
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||
// be passed around as a Element. We therefore do one last carry chain,
|
||||
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
}
|
||||
|
||||
func feSquareGeneric(v, a *Element) {
|
||||
l0 := a.l0
|
||||
l1 := a.l1
|
||||
l2 := a.l2
|
||||
l3 := a.l3
|
||||
l4 := a.l4
|
||||
|
||||
// Squaring works precisely like multiplication above, but thanks to its
|
||||
// symmetry we get to group a few terms together.
|
||||
//
|
||||
// l4 l3 l2 l1 l0 x
|
||||
// l4 l3 l2 l1 l0 =
|
||||
// ------------------------
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||
// ----------------------------------------------
|
||||
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||
//
|
||||
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||
// --------------------------------------
|
||||
// r4 r3 r2 r1 r0
|
||||
//
|
||||
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||
// only three Mul64 and four Add64, instead of five and eight.
|
||||
|
||||
l0_2 := l0 * 2
|
||||
l1_2 := l1 * 2
|
||||
|
||||
l1_38 := l1 * 38
|
||||
l2_38 := l2 * 38
|
||||
l3_38 := l3 * 38
|
||||
|
||||
l3_19 := l3 * 19
|
||||
l4_19 := l4 * 19
|
||||
|
||||
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||
r0 := mul64(l0, l0)
|
||||
r0 = addMul64(r0, l1_38, l4)
|
||||
r0 = addMul64(r0, l2_38, l3)
|
||||
|
||||
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||
r1 := mul64(l0_2, l1)
|
||||
r1 = addMul64(r1, l2_38, l4)
|
||||
r1 = addMul64(r1, l3_19, l3)
|
||||
|
||||
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||
r2 := mul64(l0_2, l2)
|
||||
r2 = addMul64(r2, l1, l1)
|
||||
r2 = addMul64(r2, l3_38, l4)
|
||||
|
||||
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||
r3 := mul64(l0_2, l3)
|
||||
r3 = addMul64(r3, l1_2, l2)
|
||||
r3 = addMul64(r3, l4_19, l4)
|
||||
|
||||
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||
r4 := mul64(l0_2, l4)
|
||||
r4 = addMul64(r4, l1_2, l3)
|
||||
r4 = addMul64(r4, l2, l2)
|
||||
|
||||
c0 := shiftRightBy51(r0)
|
||||
c1 := shiftRightBy51(r1)
|
||||
c2 := shiftRightBy51(r2)
|
||||
c3 := shiftRightBy51(r3)
|
||||
c4 := shiftRightBy51(r4)
|
||||
|
||||
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||
rr1 := r1.lo&maskLow51Bits + c0
|
||||
rr2 := r2.lo&maskLow51Bits + c1
|
||||
rr3 := r3.lo&maskLow51Bits + c2
|
||||
rr4 := r4.lo&maskLow51Bits + c3
|
||||
|
||||
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||
v.carryPropagate()
|
||||
}
|
||||
|
||||
// carryPropagate brings the limbs below 52 bits by applying the reduction
|
||||
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
||||
func (v *Element) carryPropagateGeneric() *Element {
|
||||
c0 := v.l0 >> 51
|
||||
c1 := v.l1 >> 51
|
||||
c2 := v.l2 >> 51
|
||||
c3 := v.l3 >> 51
|
||||
c4 := v.l4 >> 51
|
||||
|
||||
// c4 is at most 64 - 51 = 13 bits, so c4*19 is at most 18 bits, and
|
||||
// the final l0 will be at most 52 bits. Similarly for the rest.
|
||||
v.l0 = v.l0&maskLow51Bits + c4*19
|
||||
v.l1 = v.l1&maskLow51Bits + c0
|
||||
v.l2 = v.l2&maskLow51Bits + c1
|
||||
v.l3 = v.l3&maskLow51Bits + c2
|
||||
v.l4 = v.l4&maskLow51Bits + c3
|
||||
|
||||
return v
|
||||
}
|
1030
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
Normal file
1030
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
Normal file
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package edwards25519
|
||||
|
||||
import "sync"
|
||||
|
||||
// basepointTable is a set of 32 affineLookupTables, where table i is generated
|
||||
// from 256i * basepoint. It is precomputed the first time it's used.
|
||||
func basepointTable() *[32]affineLookupTable {
|
||||
basepointTablePrecomp.initOnce.Do(func() {
|
||||
p := NewGeneratorPoint()
|
||||
for i := 0; i < 32; i++ {
|
||||
basepointTablePrecomp.table[i].FromP3(p)
|
||||
for j := 0; j < 8; j++ {
|
||||
p.Add(p, p)
|
||||
}
|
||||
}
|
||||
})
|
||||
return &basepointTablePrecomp.table
|
||||
}
|
||||
|
||||
var basepointTablePrecomp struct {
|
||||
table [32]affineLookupTable
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
// ScalarBaseMult sets v = x * B, where B is the canonical generator, and
|
||||
// returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
|
||||
basepointTable := basepointTable()
|
||||
|
||||
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
|
||||
// as described in the Ed25519 paper
|
||||
//
|
||||
// Group even and odd coefficients
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
|
||||
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
|
||||
//
|
||||
// We use a lookup table for each i to get x_i*16^(2*i)*B
|
||||
// and do four doublings to multiply by 16.
|
||||
digits := x.signedRadix16()
|
||||
|
||||
multiple := &affineCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
|
||||
// Accumulate the odd components first
|
||||
v.Set(NewIdentityPoint())
|
||||
for i := 1; i < 64; i += 2 {
|
||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||
tmp1.AddAffine(v, multiple)
|
||||
v.fromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
// Multiply by 16
|
||||
tmp2.FromP3(v) // tmp2 = v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // now v = 16*(odd components)
|
||||
|
||||
// Accumulate the even components
|
||||
for i := 0; i < 64; i += 2 {
|
||||
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||
tmp1.AddAffine(v, multiple)
|
||||
v.fromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// ScalarMult sets v = x * q, and returns v.
|
||||
//
|
||||
// The scalar multiplication is done in constant time.
|
||||
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
|
||||
checkInitialized(q)
|
||||
|
||||
var table projLookupTable
|
||||
table.FromP3(q)
|
||||
|
||||
// Write x = sum(x_i * 16^i)
|
||||
// so x*Q = sum( Q*x_i*16^i )
|
||||
// = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... )
|
||||
// <------compute inside out---------
|
||||
//
|
||||
// We use the lookup table to get the x_i*Q values
|
||||
// and do four doublings to compute 16*Q
|
||||
digits := x.signedRadix16()
|
||||
|
||||
// Unwrap first loop iteration to save computing 16*identity
|
||||
multiple := &projCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
table.SelectInto(multiple, digits[63])
|
||||
|
||||
v.Set(NewIdentityPoint())
|
||||
tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords
|
||||
for i := 62; i >= 0; i-- {
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||
table.SelectInto(multiple, digits[i])
|
||||
tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords
|
||||
}
|
||||
v.fromP1xP1(tmp1)
|
||||
return v
|
||||
}
|
||||
|
||||
// basepointNafTable is the nafLookupTable8 for the basepoint.
|
||||
// It is precomputed the first time it's used.
|
||||
func basepointNafTable() *nafLookupTable8 {
|
||||
basepointNafTablePrecomp.initOnce.Do(func() {
|
||||
basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
|
||||
})
|
||||
return &basepointNafTablePrecomp.table
|
||||
}
|
||||
|
||||
var basepointNafTablePrecomp struct {
|
||||
table nafLookupTable8
|
||||
initOnce sync.Once
|
||||
}
|
||||
|
||||
// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical
|
||||
// generator, and returns v.
|
||||
//
|
||||
// Execution time depends on the inputs.
|
||||
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
|
||||
checkInitialized(A)
|
||||
|
||||
// Similarly to the single variable-base approach, we compute
|
||||
// digits and use them with a lookup table. However, because
|
||||
// we are allowed to do variable-time operations, we don't
|
||||
// need constant-time lookups or constant-time digit
|
||||
// computations.
|
||||
//
|
||||
// So we use a non-adjacent form of some width w instead of
|
||||
// radix 16. This is like a binary representation (one digit
|
||||
// for each binary place) but we allow the digits to grow in
|
||||
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
||||
// sparse as possible. Intuitively, this "condenses" the
|
||||
// "mass" of the scalar onto sparse coefficients (meaning
|
||||
// fewer additions).
|
||||
|
||||
basepointNafTable := basepointNafTable()
|
||||
var aTable nafLookupTable5
|
||||
aTable.FromP3(A)
|
||||
// Because the basepoint is fixed, we can use a wider NAF
|
||||
// corresponding to a bigger table.
|
||||
aNaf := a.nonAdjacentForm(5)
|
||||
bNaf := b.nonAdjacentForm(8)
|
||||
|
||||
// Find the first nonzero coefficient.
|
||||
i := 255
|
||||
for j := i; j >= 0; j-- {
|
||||
if aNaf[j] != 0 || bNaf[j] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
multA := &projCached{}
|
||||
multB := &affineCached{}
|
||||
tmp1 := &projP1xP1{}
|
||||
tmp2 := &projP2{}
|
||||
tmp2.Zero()
|
||||
|
||||
// Move from high to low bits, doubling the accumulator
|
||||
// at each iteration and checking whether there is a nonzero
|
||||
// coefficient to look up a multiple of.
|
||||
for ; i >= 0; i-- {
|
||||
tmp1.Double(tmp2)
|
||||
|
||||
// Only update v if we have a nonzero coeff to add in.
|
||||
if aNaf[i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
aTable.SelectInto(multA, aNaf[i])
|
||||
tmp1.Add(v, multA)
|
||||
} else if aNaf[i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
aTable.SelectInto(multA, -aNaf[i])
|
||||
tmp1.Sub(v, multA)
|
||||
}
|
||||
|
||||
if bNaf[i] > 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
basepointNafTable.SelectInto(multB, bNaf[i])
|
||||
tmp1.AddAffine(v, multB)
|
||||
} else if bNaf[i] < 0 {
|
||||
v.fromP1xP1(tmp1)
|
||||
basepointNafTable.SelectInto(multB, -bNaf[i])
|
||||
tmp1.SubAffine(v, multB)
|
||||
}
|
||||
|
||||
tmp2.FromP1xP1(tmp1)
|
||||
}
|
||||
|
||||
v.fromP2(tmp2)
|
||||
return v
|
||||
}
|
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
Normal file
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package edwards25519
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
)
|
||||
|
||||
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||
type projLookupTable struct {
|
||||
points [8]projCached
|
||||
}
|
||||
|
||||
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
||||
type affineLookupTable struct {
|
||||
points [8]affineCached
|
||||
}
|
||||
|
||||
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
||||
type nafLookupTable5 struct {
|
||||
points [8]projCached
|
||||
}
|
||||
|
||||
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
||||
type nafLookupTable8 struct {
|
||||
points [64]affineCached
|
||||
}
|
||||
|
||||
// Constructors.
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
func (v *projLookupTable) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].FromP3(q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
|
||||
// This is needlessly complicated because the API has explicit
|
||||
// recievers instead of creating stack objects and relying on RVO
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
func (v *affineLookupTable) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||
v.points[0].FromP3(q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Builds a lookup table at runtime. Fast.
|
||||
func (v *nafLookupTable5) FromP3(q *Point) {
|
||||
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
||||
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
||||
v.points[0].FromP3(q)
|
||||
q2 := Point{}
|
||||
q2.Add(q, q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 7; i++ {
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||
func (v *nafLookupTable8) FromP3(q *Point) {
|
||||
v.points[0].FromP3(q)
|
||||
q2 := Point{}
|
||||
q2.Add(q, q)
|
||||
tmpP3 := Point{}
|
||||
tmpP1xP1 := projP1xP1{}
|
||||
for i := 0; i < 63; i++ {
|
||||
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i])))
|
||||
}
|
||||
}
|
||||
|
||||
// Selectors.
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
func (v *projLookupTable) SelectInto(dest *projCached, x int8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := uint8((x + xmask) ^ xmask)
|
||||
|
||||
dest.Zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||
dest.Select(&v.points[j-1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.CondNeg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||
func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) {
|
||||
// Compute xabs = |x|
|
||||
xmask := x >> 7
|
||||
xabs := uint8((x + xmask) ^ xmask)
|
||||
|
||||
dest.Zero()
|
||||
for j := 1; j <= 8; j++ {
|
||||
// Set dest = j*Q if |x| = j
|
||||
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||
dest.Select(&v.points[j-1], dest, cond)
|
||||
}
|
||||
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||
dest.CondNeg(int(xmask & 1))
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
||||
func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) {
|
||||
*dest = v.points[x/2]
|
||||
}
|
||||
|
||||
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
||||
func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) {
|
||||
*dest = v.points[x/2]
|
||||
}
|
4
vendor/github.com/ManyakRus/logrus/.gitignore
generated
vendored
Normal file
4
vendor/github.com/ManyakRus/logrus/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
logrus
|
||||
vendor
|
||||
|
||||
.idea/
|
40
vendor/github.com/ManyakRus/logrus/.golangci.yml
generated
vendored
Normal file
40
vendor/github.com/ManyakRus/logrus/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
run:
|
||||
# do not run on test files yet
|
||||
tests: false
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
lll:
|
||||
line-length: 100
|
||||
tab-width: 4
|
||||
|
||||
prealloc:
|
||||
simple: false
|
||||
range-loops: false
|
||||
for-loops: false
|
||||
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- megacheck
|
||||
- govet
|
||||
disable:
|
||||
- maligned
|
||||
- prealloc
|
||||
disable-all: false
|
||||
presets:
|
||||
- bugs
|
||||
- unused
|
||||
fast: false
|
15
vendor/github.com/ManyakRus/logrus/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/ManyakRus/logrus/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
language: go
|
||||
go_import_path: github.com/sirupsen/logrus
|
||||
git:
|
||||
depth: 1
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
go: 1.15.x
|
||||
os: linux
|
||||
install:
|
||||
- ./travis/install.sh
|
||||
script:
|
||||
- cd ci
|
||||
- go run mage.go -v -w ../ crossBuild
|
||||
- go run mage.go -v -w ../ lint
|
||||
- go run mage.go -v -w ../ test
|
259
vendor/github.com/ManyakRus/logrus/CHANGELOG.md
generated
vendored
Normal file
259
vendor/github.com/ManyakRus/logrus/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
# 1.8.1
|
||||
Code quality:
|
||||
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
||||
* improve timestamp format documentation
|
||||
|
||||
Fixes:
|
||||
* fix race condition on logger hooks
|
||||
|
||||
|
||||
# 1.8.0
|
||||
|
||||
Correct versioning number replacing v1.7.1.
|
||||
|
||||
# 1.7.1
|
||||
|
||||
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
||||
|
||||
Code quality:
|
||||
* use go 1.15 in travis
|
||||
* use magefile as task runner
|
||||
|
||||
Fixes:
|
||||
* small fixes about new go 1.13 error formatting system
|
||||
* Fix for long time race condiction with mutating data hooks
|
||||
|
||||
Features:
|
||||
* build support for zos
|
||||
|
||||
# 1.7.0
|
||||
Fixes:
|
||||
* the dependency toward a windows terminal library has been removed
|
||||
|
||||
Features:
|
||||
* a new buffer pool management API has been added
|
||||
* a set of `<LogLevel>Fn()` functions have been added
|
||||
|
||||
# 1.6.0
|
||||
Fixes:
|
||||
* end of line cleanup
|
||||
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
|
||||
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
|
||||
|
||||
Features:
|
||||
* add an option to the `TextFormatter` to completely disable fields quoting
|
||||
|
||||
# 1.5.0
|
||||
Code quality:
|
||||
* add golangci linter run on travis
|
||||
|
||||
Fixes:
|
||||
* add mutex for hooks concurrent access on `Entry` data
|
||||
* caller function field for go1.14
|
||||
* fix build issue for gopherjs target
|
||||
|
||||
Feature:
|
||||
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
|
||||
* add a `DisableHTMLEscape` option in the `JSONFormatter`
|
||||
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
|
||||
|
||||
# 1.4.2
|
||||
* Fixes build break for plan9, nacl, solaris
|
||||
# 1.4.1
|
||||
This new release introduces:
|
||||
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
||||
* Remove dependency on golang.org/x/crypto (#932, #943)
|
||||
|
||||
Fixes:
|
||||
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
||||
|
||||
# 1.4.0
|
||||
This new release introduces:
|
||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
|
||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
||||
|
||||
Fixes:
|
||||
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
||||
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
||||
* Fix infinite recursion on unknown `Level.String()` (#907)
|
||||
* Fix race condition in `getCaller` (#916).
|
||||
|
||||
|
||||
# 1.3.0
|
||||
This new release introduces:
|
||||
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
||||
|
||||
Fixes:
|
||||
* Building prometheus node_exporter on AIX (#840)
|
||||
* Race condition in TextFormatter (#468)
|
||||
* Travis CI import path (#868)
|
||||
* Remove coloured output on Windows (#862)
|
||||
* Pointer to func as field in JSONFormatter (#870)
|
||||
* Properly marshal Levels (#873)
|
||||
|
||||
# 1.2.0
|
||||
This new release introduces:
|
||||
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
||||
* A new trace level named `Trace` whose level is below `Debug`
|
||||
* A configurable exit function to be called upon a Fatal trace
|
||||
* The `Level` object now implements `encoding.TextUnmarshaler` interface
|
||||
|
||||
# 1.1.1
|
||||
This is a bug fix release.
|
||||
* fix the build break on Solaris
|
||||
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
||||
|
||||
# 1.1.0
|
||||
This new release introduces:
|
||||
* several fixes:
|
||||
* a fix for a race condition on entry formatting
|
||||
* proper cleanup of previously used entries before putting them back in the pool
|
||||
* the extra new line at the end of message in text formatter has been removed
|
||||
* a new global public API to check if a level is activated: IsLevelEnabled
|
||||
* the following methods have been added to the Logger object
|
||||
* IsLevelEnabled
|
||||
* SetFormatter
|
||||
* SetOutput
|
||||
* ReplaceHooks
|
||||
* introduction of go module
|
||||
* an indent configuration for the json formatter
|
||||
* output colour support for windows
|
||||
* the field sort function is now configurable for text formatter
|
||||
* the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
|
||||
|
||||
# 1.0.6
|
||||
|
||||
This new release introduces:
|
||||
* a new api WithTime which allows to easily force the time of the log entry
|
||||
which is mostly useful for logger wrapper
|
||||
* a fix reverting the immutability of the entry given as parameter to the hooks
|
||||
a new configuration field of the json formatter in order to put all the fields
|
||||
in a nested dictionnary
|
||||
* a new SetOutput method in the Logger
|
||||
* a new configuration of the textformatter to configure the name of the default keys
|
||||
* a new configuration of the text formatter to disable the level truncation
|
||||
|
||||
# 1.0.5
|
||||
|
||||
* Fix hooks race (#707)
|
||||
* Fix panic deadlock (#695)
|
||||
|
||||
# 1.0.4
|
||||
|
||||
* Fix race when adding hooks (#612)
|
||||
* Fix terminal check in AppEngine (#635)
|
||||
|
||||
# 1.0.3
|
||||
|
||||
* Replace example files with testable examples
|
||||
|
||||
# 1.0.2
|
||||
|
||||
* bug: quote non-string values in text formatter (#583)
|
||||
* Make (*Logger) SetLevel a public method
|
||||
|
||||
# 1.0.1
|
||||
|
||||
* bug: fix escaping in text formatter (#575)
|
||||
|
||||
# 1.0.0
|
||||
|
||||
* Officially changed name to lower-case
|
||||
* bug: colors on Windows 10 (#541)
|
||||
* bug: fix race in accessing level (#512)
|
||||
|
||||
# 0.11.5
|
||||
|
||||
* feature: add writer and writerlevel to entry (#372)
|
||||
|
||||
# 0.11.4
|
||||
|
||||
* bug: fix undefined variable on solaris (#493)
|
||||
|
||||
# 0.11.3
|
||||
|
||||
* formatter: configure quoting of empty values (#484)
|
||||
* formatter: configure quoting character (default is `"`) (#484)
|
||||
* bug: fix not importing io correctly in non-linux environments (#481)
|
||||
|
||||
# 0.11.2
|
||||
|
||||
* bug: fix windows terminal detection (#476)
|
||||
|
||||
# 0.11.1
|
||||
|
||||
* bug: fix tty detection with custom out (#471)
|
||||
|
||||
# 0.11.0
|
||||
|
||||
* performance: Use bufferpool to allocate (#370)
|
||||
* terminal: terminal detection for app-engine (#343)
|
||||
* feature: exit handler (#375)
|
||||
|
||||
# 0.10.0
|
||||
|
||||
* feature: Add a test hook (#180)
|
||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||
* performance: avoid re-allocations on `WithFields` (#335)
|
||||
|
||||
# 0.9.0
|
||||
|
||||
* logrus/text_formatter: don't emit empty msg
|
||||
* logrus/hooks/airbrake: move out of main repository
|
||||
* logrus/hooks/sentry: move out of main repository
|
||||
* logrus/hooks/papertrail: move out of main repository
|
||||
* logrus/hooks/bugsnag: move out of main repository
|
||||
* logrus/core: run tests with `-race`
|
||||
* logrus/core: detect TTY based on `stderr`
|
||||
* logrus/core: support `WithError` on logger
|
||||
* logrus/core: Solaris support
|
||||
|
||||
# 0.8.7
|
||||
|
||||
* logrus/core: fix possible race (#216)
|
||||
* logrus/doc: small typo fixes and doc improvements
|
||||
|
||||
|
||||
# 0.8.6
|
||||
|
||||
* hooks/raven: allow passing an initialized client
|
||||
|
||||
# 0.8.5
|
||||
|
||||
* logrus/core: revert #208
|
||||
|
||||
# 0.8.4
|
||||
|
||||
* formatter/text: fix data race (#218)
|
||||
|
||||
# 0.8.3
|
||||
|
||||
* logrus/core: fix entry log level (#208)
|
||||
* logrus/core: improve performance of text formatter by 40%
|
||||
* logrus/core: expose `LevelHooks` type
|
||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||
* formatter/text: print structs more verbosely
|
||||
|
||||
# 0.8.2
|
||||
|
||||
* logrus: fix more Fatal family functions
|
||||
|
||||
# 0.8.1
|
||||
|
||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||
|
||||
# 0.8.0
|
||||
|
||||
* logrus: defaults to stderr instead of stdout
|
||||
* hooks/sentry: add special field for `*http.Request`
|
||||
* formatter/text: ignore Windows for colors
|
||||
|
||||
# 0.7.3
|
||||
|
||||
* formatter/\*: allow configuration of timestamp layout
|
||||
|
||||
# 0.7.2
|
||||
|
||||
* formatter/text: Add configuration option for time format (#158)
|
21
vendor/github.com/ManyakRus/logrus/LICENSE
generated
vendored
Normal file
21
vendor/github.com/ManyakRus/logrus/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Simon Eskildsen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
515
vendor/github.com/ManyakRus/logrus/README.md
generated
vendored
Normal file
515
vendor/github.com/ManyakRus/logrus/README.md
generated
vendored
Normal file
@ -0,0 +1,515 @@
|
||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [![Build Status](https://github.com/sirupsen/logrus/workflows/CI/badge.svg)](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [![Build Status](https://travis-ci.org/sirupsen/logrus.svg?branch=master)](https://travis-ci.org/sirupsen/logrus) [![Go Reference](https://pkg.go.dev/badge/github.com/sirupsen/logrus.svg)](https://pkg.go.dev/github.com/sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger.
|
||||
|
||||
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
|
||||
simply too hard to do in a way that won't break many people's projects, which is
|
||||
the last thing you want from your Logging library (again...).
|
||||
|
||||
This does not mean Logrus is dead. Logrus will continue to be maintained for
|
||||
security, (backwards compatible) bug fixes, and performance (where we are
|
||||
limited by the interface).
|
||||
|
||||
I believe Logrus' biggest contribution is to have played a part in today's
|
||||
widespread use of structured logging in Golang. There doesn't seem to be a
|
||||
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
|
||||
community has built those independently. Many fantastic alternatives have sprung
|
||||
up. Logrus would look like those, had it been re-designed with what we know
|
||||
about structured logging in Go today. Check out, for example,
|
||||
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
|
||||
|
||||
[zerolog]: https://github.com/rs/zerolog
|
||||
[zap]: https://github.com/uber-go/zap
|
||||
[apex]: https://github.com/apex/log
|
||||
|
||||
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
||||
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
||||
this caused issues in the community and we needed a standard. Some environments
|
||||
experienced problems with the upper-case variant, so the lower-case was decided.
|
||||
Everything using `logrus` will need to use the lower-case:
|
||||
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
||||
|
||||
To fix Glide, see [these
|
||||
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||
For an in-depth explanation of the casing issue, see [this
|
||||
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
||||
|
||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||
plain text):
|
||||
|
||||
![Colored](http://i.imgur.com/PY7qMwd.png)
|
||||
|
||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||
|
||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||
|
||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||
|
||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||
|
||||
```text
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||
```
|
||||
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
|
||||
|
||||
```go
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableColors: true,
|
||||
FullTimestamp: true,
|
||||
})
|
||||
```
|
||||
|
||||
#### Logging Method Name
|
||||
|
||||
If you wish to add the calling method as a field, instruct the logger via:
|
||||
```go
|
||||
log.SetReportCaller(true)
|
||||
```
|
||||
This adds the caller as 'method' like so:
|
||||
|
||||
```json
|
||||
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
|
||||
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
|
||||
```
|
||||
|
||||
```text
|
||||
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
|
||||
```
|
||||
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
||||
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
||||
environment via benchmarks:
|
||||
```
|
||||
go test -bench=.*CallerTracing
|
||||
```
|
||||
|
||||
|
||||
#### Case-sensitivity
|
||||
|
||||
The organization's name was changed to lower-case--and this will not be changed
|
||||
back. If you are getting import conflicts due to case sensitivity, please use
|
||||
the lower-case import: `github.com/sirupsen/logrus`.
|
||||
|
||||
#### Example
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
```
|
||||
|
||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||
want:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Log as JSON instead of the default ASCII formatter.
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
|
||||
// Output to stdout instead of the default stderr
|
||||
// Can be any io.Writer, see below for File example
|
||||
log.SetOutput(os.Stdout)
|
||||
|
||||
// Only log the warning severity or above.
|
||||
log.SetLevel(log.WarnLevel)
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 122,
|
||||
}).Warn("The group's number increased tremendously!")
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
"omg": true,
|
||||
"number": 100,
|
||||
}).Fatal("The ice breaks!")
|
||||
|
||||
// A common pattern is to re-use fields between logging statements by re-using
|
||||
// the logrus.Entry returned from WithFields()
|
||||
contextLogger := log.WithFields(log.Fields{
|
||||
"common": "this is a common field",
|
||||
"other": "I also should be logged always",
|
||||
})
|
||||
|
||||
contextLogger.Info("I'll be logged with common and other field")
|
||||
contextLogger.Info("Me too")
|
||||
}
|
||||
```
|
||||
|
||||
For more advanced usage such as logging to multiple locations from the same
|
||||
application, you can also create an instance of the `logrus` Logger:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Create a new instance of the logger. You can have any number of instances.
|
||||
var log = logrus.New()
|
||||
|
||||
func main() {
|
||||
// The API for setting attributes is a little different than the package level
|
||||
// exported logger. See Godoc.
|
||||
log.Out = os.Stdout
|
||||
|
||||
// You could set this to any `io.Writer` such as a file
|
||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||
// if err == nil {
|
||||
// log.Out = file
|
||||
// } else {
|
||||
// log.Info("Failed to log to file, using default stderr")
|
||||
// }
|
||||
|
||||
log.WithFields(logrus.Fields{
|
||||
"animal": "walrus",
|
||||
"size": 10,
|
||||
}).Info("A group of walrus emerges from the ocean")
|
||||
}
|
||||
```
|
||||
|
||||
#### Fields
|
||||
|
||||
Logrus encourages careful, structured logging through logging fields instead of
|
||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
||||
to send event %s to topic %s with key %d")`, you should log the much more
|
||||
discoverable:
|
||||
|
||||
```go
|
||||
log.WithFields(log.Fields{
|
||||
"event": event,
|
||||
"topic": topic,
|
||||
"key": key,
|
||||
}).Fatal("Failed to send event")
|
||||
```
|
||||
|
||||
We've found this API forces you to think about logging in a way that produces
|
||||
much more useful logging messages. We've been in countless situations where just
|
||||
a single added field to a log statement that was already there would've saved us
|
||||
hours. The `WithFields` call is optional.
|
||||
|
||||
In general, with Logrus using any of the `printf`-family functions should be
|
||||
seen as a hint you should add a field, however, you can still use the
|
||||
`printf`-family functions with Logrus.
|
||||
|
||||
#### Default Fields
|
||||
|
||||
Often it's helpful to have fields _always_ attached to log statements in an
|
||||
application or parts of one. For example, you may want to always log the
|
||||
`request_id` and `user_ip` in the context of a request. Instead of writing
|
||||
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
||||
every line, you can create a `logrus.Entry` to pass around instead:
|
||||
|
||||
```go
|
||||
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
||||
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
||||
requestLogger.Warn("something not great happened")
|
||||
```
|
||||
|
||||
#### Hooks
|
||||
|
||||
You can add hooks for logging levels. For example to send errors to an exception
|
||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||
multiple places simultaneously, e.g. syslog.
|
||||
|
||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||
`init`:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||
|
||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||
if err != nil {
|
||||
log.Error("Unable to connect to local syslog daemon")
|
||||
} else {
|
||||
log.AddHook(hook)
|
||||
}
|
||||
}
|
||||
```
|
||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||
|
||||
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
||||
|
||||
|
||||
#### Level logging
|
||||
|
||||
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
|
||||
|
||||
```go
|
||||
log.Trace("Something very low level.")
|
||||
log.Debug("Useful debugging information.")
|
||||
log.Info("Something noteworthy happened!")
|
||||
log.Warn("You should probably take a look at this.")
|
||||
log.Error("Something failed but I'm not quitting.")
|
||||
// Calls os.Exit(1) after logging
|
||||
log.Fatal("Bye.")
|
||||
// Calls panic() after logging
|
||||
log.Panic("I'm bailing.")
|
||||
```
|
||||
|
||||
You can set the logging level on a `Logger`, then it will only log entries with
|
||||
that severity or anything above it:
|
||||
|
||||
```go
|
||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||
log.SetLevel(log.InfoLevel)
|
||||
```
|
||||
|
||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
||||
environment if your application has that.
|
||||
|
||||
Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
|
||||
|
||||
#### Entries
|
||||
|
||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||
automatically added to all logging events:
|
||||
|
||||
1. `time`. The timestamp when the entry was created.
|
||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||
the `AddFields` call. E.g. `Failed to send event.`
|
||||
3. `level`. The logging level. E.g. `info`.
|
||||
|
||||
#### Environments
|
||||
|
||||
Logrus has no notion of environment.
|
||||
|
||||
If you wish for hooks and formatters to only be used in specific environments,
|
||||
you should handle that yourself. For example, if your application has a global
|
||||
variable `Environment`, which is a string representation of the environment you
|
||||
could do:
|
||||
|
||||
```go
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// do something here to set environment depending on an environment variable
|
||||
// or command-line flag
|
||||
if Environment == "production" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
} else {
|
||||
// The TextFormatter is default, you don't actually have to do this.
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This configuration is how `logrus` was intended to be used, but JSON in
|
||||
production is mostly only useful if you do log aggregation with tools like
|
||||
Splunk or Logstash.
|
||||
|
||||
#### Formatters
|
||||
|
||||
The built-in logging formatters are:
|
||||
|
||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||
without colors.
|
||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||
field to `true`. To force no colored output even if there is a TTY set the
|
||||
`DisableColors` field to `true`. For Windows, see
|
||||
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
||||
truncation set the `DisableLevelTruncation` field to `true`.
|
||||
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
|
||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
||||
|
||||
Third party logging formatters:
|
||||
|
||||
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
|
||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
||||
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
|
||||
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
|
||||
|
||||
You can define your formatter by implementing the `Formatter` interface,
|
||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||
default ones (see Entries section above):
|
||||
|
||||
```go
|
||||
type MyJSONFormatter struct {
|
||||
}
|
||||
|
||||
log.SetFormatter(new(MyJSONFormatter))
|
||||
|
||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
// Note this doesn't include Time, Level and Message which are available on
|
||||
// the Entry. Consult `godoc` on information about those fields or read the
|
||||
// source of the official loggers.
|
||||
serialized, err := json.Marshal(entry.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Logger as an `io.Writer`
|
||||
|
||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||
|
||||
```go
|
||||
w := logger.Writer()
|
||||
defer w.Close()
|
||||
|
||||
srv := http.Server{
|
||||
// create a stdlib log.Logger that writes to
|
||||
// logrus.Logger.
|
||||
ErrorLog: log.New(w, "", 0),
|
||||
}
|
||||
```
|
||||
|
||||
Each line written to that writer will be printed the usual way, using formatters
|
||||
and hooks. The level for those entries is `info`.
|
||||
|
||||
This means that we can override the standard library logger easily:
|
||||
|
||||
```go
|
||||
logger := logrus.New()
|
||||
logger.Formatter = &logrus.JSONFormatter{}
|
||||
|
||||
// Use logrus for standard log output
|
||||
// Note that `log` here references stdlib's log
|
||||
// Not logrus imported under the name `log`.
|
||||
log.SetOutput(logger.Writer())
|
||||
```
|
||||
|
||||
#### Rotation
|
||||
|
||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||
external program (like `logrotate(8)`) that can compress and delete old log
|
||||
entries. It should not be a feature of the application-level logger.
|
||||
|
||||
#### Tools
|
||||
|
||||
| Tool | Description |
|
||||
| ---- | ----------- |
|
||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.|
|
||||
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
||||
|
||||
#### Testing
|
||||
|
||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||
|
||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
|
||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||
|
||||
```go
|
||||
import(
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSomething(t*testing.T){
|
||||
logger, hook := test.NewNullLogger()
|
||||
logger.Error("Helloerror")
|
||||
|
||||
assert.Equal(t, 1, len(hook.Entries))
|
||||
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(t, hook.LastEntry())
|
||||
}
|
||||
```
|
||||
|
||||
#### Fatal handlers
|
||||
|
||||
Logrus can register one or more functions that will be called when any `fatal`
|
||||
level message is logged. The registered handlers will be executed before
|
||||
logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need
|
||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||
|
||||
```
|
||||
...
|
||||
handler := func() {
|
||||
// gracefully shutdown something...
|
||||
}
|
||||
logrus.RegisterExitHandler(handler)
|
||||
...
|
||||
```
|
||||
|
||||
#### Thread safety
|
||||
|
||||
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
||||
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
||||
|
||||
Situation when locking is not needed includes:
|
||||
|
||||
* You have no hooks registered, or hooks calling is already thread-safe.
|
||||
|
||||
* Writing to logger.Out is already thread-safe, for example:
|
||||
|
||||
1) logger.Out is protected by locks.
|
||||
|
||||
2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)
|
||||
|
||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
76
vendor/github.com/ManyakRus/logrus/alt_exit.go
generated
vendored
Normal file
76
vendor/github.com/ManyakRus/logrus/alt_exit.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://github.com/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
|
||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
||||
// any Fatal log entry is made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
|
||||
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
|
||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
||||
// any Fatal log entry is made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func DeferExitHandler(handler func()) {
|
||||
handlers = append([]func(){handler}, handlers...)
|
||||
}
|
14
vendor/github.com/ManyakRus/logrus/appveyor.yml
generated
vendored
Normal file
14
vendor/github.com/ManyakRus/logrus/appveyor.yml
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
version: "{build}"
|
||||
platform: x64
|
||||
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
||||
environment:
|
||||
GOPATH: c:\gopath
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- go version
|
||||
build_script:
|
||||
- go get -t
|
||||
- go test
|
43
vendor/github.com/ManyakRus/logrus/buffer_pool.go
generated
vendored
Normal file
43
vendor/github.com/ManyakRus/logrus/buffer_pool.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
bufferPool BufferPool
|
||||
)
|
||||
|
||||
type BufferPool interface {
|
||||
Put(*bytes.Buffer)
|
||||
Get() *bytes.Buffer
|
||||
}
|
||||
|
||||
type defaultPool struct {
|
||||
pool *sync.Pool
|
||||
}
|
||||
|
||||
func (p *defaultPool) Put(buf *bytes.Buffer) {
|
||||
p.pool.Put(buf)
|
||||
}
|
||||
|
||||
func (p *defaultPool) Get() *bytes.Buffer {
|
||||
return p.pool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
// SetBufferPool allows to replace the default logrus buffer pool
|
||||
// to better meets the specific needs of an application.
|
||||
func SetBufferPool(bp BufferPool) {
|
||||
bufferPool = bp
|
||||
}
|
||||
|
||||
func init() {
|
||||
SetBufferPool(&defaultPool{
|
||||
pool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
26
vendor/github.com/ManyakRus/logrus/doc.go
generated
vendored
Normal file
26
vendor/github.com/ManyakRus/logrus/doc.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||
|
||||
|
||||
The simplest way to use Logrus is simply the package-level exported logger:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.WithFields(log.Fields{
|
||||
"animal": "walrus",
|
||||
"number": 1,
|
||||
"size": 10,
|
||||
}).Info("A walrus appears")
|
||||
}
|
||||
|
||||
Output:
|
||||
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||
|
||||
For a full guide visit https://github.com/sirupsen/logrus
|
||||
*/
|
||||
package logrus
|
443
vendor/github.com/ManyakRus/logrus/entry.go
generated
vendored
Normal file
443
vendor/github.com/ManyakRus/logrus/entry.go
generated
vendored
Normal file
@ -0,0 +1,443 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// qualified package name, cached at first use
|
||||
logrusPackage string
|
||||
|
||||
// Positions in the call stack when tracing to report the calling method
|
||||
minimumCallerDepth int
|
||||
|
||||
// Used for caller information initialisation
|
||||
callerInitOnce sync.Once
|
||||
)
|
||||
|
||||
const (
|
||||
maximumCallerDepth int = 25
|
||||
knownLogrusFrames int = 4
|
||||
)
|
||||
|
||||
func init() {
|
||||
// start at the bottom of the stack before the package-name cache is primed
|
||||
minimumCallerDepth = 1
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
||||
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
|
||||
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
|
||||
// reused and passed around as much as you wish to avoid field duplication.
|
||||
type Entry struct {
|
||||
Logger *Logger
|
||||
|
||||
// Contains all the fields set by the user.
|
||||
Data Fields
|
||||
|
||||
// Time at which the log entry was created
|
||||
Time time.Time
|
||||
|
||||
// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
|
||||
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
||||
Level Level
|
||||
|
||||
// Calling method, with package name
|
||||
Caller *runtime.Frame
|
||||
|
||||
// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), a Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
|
||||
// Contains the context set by the user. Useful for hook processing etc.
|
||||
Context context.Context
|
||||
|
||||
// err may contain a field formatting error
|
||||
err string
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
return &Entry{
|
||||
Logger: logger,
|
||||
// Default is three fields, plus one optional. Give a little extra room.
|
||||
Data: make(Fields, 6),
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Dup() *Entry {
|
||||
data := make(Fields, len(entry.Data))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
||||
}
|
||||
|
||||
// Returns the bytes representation of this entry from the formatter.
|
||||
func (entry *Entry) Bytes() ([]byte, error) {
|
||||
return entry.Logger.Formatter.Format(entry)
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
serialized, err := entry.Bytes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
func (entry *Entry) WithError(err error) *Entry {
|
||||
return entry.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// Add a context to the Entry.
|
||||
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
||||
dataCopy := make(Fields, len(entry.Data))
|
||||
for k, v := range entry.Data {
|
||||
dataCopy[k] = v
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
||||
}
|
||||
|
||||
// Add a single field to the Entry.
|
||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||
return entry.WithFields(Fields{key: value})
|
||||
}
|
||||
|
||||
// Add a map of fields to the Entry.
|
||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
data := make(Fields, len(entry.Data)+len(fields))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
fieldErr := entry.err
|
||||
for k, v := range fields {
|
||||
isErrField := false
|
||||
if t := reflect.TypeOf(v); t != nil {
|
||||
switch {
|
||||
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
||||
isErrField = true
|
||||
}
|
||||
}
|
||||
if isErrField {
|
||||
tmp := fmt.Sprintf("can not add field %q", k)
|
||||
if fieldErr != "" {
|
||||
fieldErr = entry.err + ", " + tmp
|
||||
} else {
|
||||
fieldErr = tmp
|
||||
}
|
||||
} else {
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
||||
}
|
||||
|
||||
// Overrides the time of the Entry.
|
||||
func (entry *Entry) WithTime(t time.Time) *Entry {
|
||||
dataCopy := make(Fields, len(entry.Data))
|
||||
for k, v := range entry.Data {
|
||||
dataCopy[k] = v
|
||||
}
|
||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
||||
}
|
||||
|
||||
// getPackageName reduces a fully qualified function name to the package name
|
||||
// There really ought to be to be a better way...
|
||||
func getPackageName(f string) string {
|
||||
for {
|
||||
lastPeriod := strings.LastIndex(f, ".")
|
||||
lastSlash := strings.LastIndex(f, "/")
|
||||
if lastPeriod > lastSlash {
|
||||
f = f[:lastPeriod]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// getCaller retrieves the name of the first non-logrus calling function
|
||||
func getCaller() *runtime.Frame {
|
||||
// cache this package's fully-qualified name
|
||||
callerInitOnce.Do(func() {
|
||||
pcs := make([]uintptr, maximumCallerDepth)
|
||||
_ = runtime.Callers(0, pcs)
|
||||
|
||||
// dynamic get the package name and the minimum caller depth
|
||||
for i := 0; i < maximumCallerDepth; i++ {
|
||||
funcName := runtime.FuncForPC(pcs[i]).Name()
|
||||
if strings.Contains(funcName, "getCaller") {
|
||||
logrusPackage = getPackageName(funcName)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
minimumCallerDepth = knownLogrusFrames
|
||||
})
|
||||
|
||||
// Restrict the lookback frames to avoid runaway lookups
|
||||
pcs := make([]uintptr, maximumCallerDepth)
|
||||
depth := runtime.Callers(minimumCallerDepth, pcs)
|
||||
frames := runtime.CallersFrames(pcs[:depth])
|
||||
|
||||
for f, again := frames.Next(); again; f, again = frames.Next() {
|
||||
pkg := getPackageName(f.Function)
|
||||
|
||||
// If the caller isn't part of this package, we're done
|
||||
if pkg != logrusPackage && (f.File[len(f.File)-15:] != "logger_proxy.go") { //sanek
|
||||
return &f //nolint:scopelint
|
||||
}
|
||||
}
|
||||
|
||||
// if we got here, we failed to find the caller's context
|
||||
return nil
|
||||
}
|
||||
|
||||
func (entry Entry) HasCaller() (has bool) {
|
||||
return entry.Logger != nil &&
|
||||
entry.Logger.ReportCaller &&
|
||||
entry.Caller != nil
|
||||
}
|
||||
|
||||
func (entry *Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
|
||||
newEntry := entry.Dup()
|
||||
|
||||
if newEntry.Time.IsZero() {
|
||||
newEntry.Time = time.Now()
|
||||
}
|
||||
|
||||
newEntry.Level = level
|
||||
newEntry.Message = msg
|
||||
|
||||
newEntry.Logger.mu.Lock()
|
||||
reportCaller := newEntry.Logger.ReportCaller
|
||||
bufPool := newEntry.getBufferPool()
|
||||
newEntry.Logger.mu.Unlock()
|
||||
|
||||
if reportCaller {
|
||||
newEntry.Caller = getCaller()
|
||||
}
|
||||
|
||||
newEntry.fireHooks()
|
||||
buffer = bufPool.Get()
|
||||
defer func() {
|
||||
newEntry.Buffer = nil
|
||||
buffer.Reset()
|
||||
bufPool.Put(buffer)
|
||||
}()
|
||||
buffer.Reset()
|
||||
newEntry.Buffer = buffer
|
||||
|
||||
newEntry.write()
|
||||
|
||||
newEntry.Buffer = nil
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||
// directly here.
|
||||
if level <= PanicLevel {
|
||||
panic(entry) //sanek
|
||||
panic(newEntry)
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) getBufferPool() (pool BufferPool) {
|
||||
if entry.Logger.BufferPool != nil {
|
||||
return entry.Logger.BufferPool
|
||||
}
|
||||
return bufferPool
|
||||
}
|
||||
|
||||
func (entry *Entry) fireHooks() {
|
||||
var tmpHooks LevelHooks
|
||||
entry.Logger.mu.Lock()
|
||||
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
||||
for k, v := range entry.Logger.Hooks {
|
||||
tmpHooks[k] = v
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
|
||||
err := tmpHooks.Fire(entry.Level, entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) write() {
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
return
|
||||
}
|
||||
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Log will log a message at the level given as parameter.
|
||||
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
|
||||
func (entry *Entry) Log(level Level, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.log(level, fmt.Sprint(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Trace(args ...interface{}) {
|
||||
entry.Log(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debug(args ...interface{}) {
|
||||
entry.Log(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Print(args ...interface{}) {
|
||||
entry.Info(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Info(args ...interface{}) {
|
||||
entry.Log(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warn(args ...interface{}) {
|
||||
entry.Log(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warning(args ...interface{}) {
|
||||
entry.Warn(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Error(args ...interface{}) {
|
||||
entry.Log(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatal(args ...interface{}) {
|
||||
entry.Log(FatalLevel, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
entry.Log(PanicLevel, args...)
|
||||
}
|
||||
|
||||
// Entry Printf family functions
|
||||
|
||||
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.Log(level, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
||||
entry.Logf(TraceLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||
entry.Logf(DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||
entry.Logf(InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||
entry.Infof(format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||
entry.Logf(WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||
entry.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||
entry.Logf(ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
entry.Logf(FatalLevel, format, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
entry.Logf(PanicLevel, format, args...)
|
||||
}
|
||||
|
||||
// Entry Println family functions
|
||||
|
||||
func (entry *Entry) Logln(level Level, args ...interface{}) {
|
||||
if entry.Logger.IsLevelEnabled(level) {
|
||||
entry.Log(level, entry.sprintlnn(args...))
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *Entry) Traceln(args ...interface{}) {
|
||||
entry.Logln(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Debugln(args ...interface{}) {
|
||||
entry.Logln(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Infoln(args ...interface{}) {
|
||||
entry.Logln(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Println(args ...interface{}) {
|
||||
entry.Infoln(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warnln(args ...interface{}) {
|
||||
entry.Logln(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Warningln(args ...interface{}) {
|
||||
entry.Warnln(args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Errorln(args ...interface{}) {
|
||||
entry.Logln(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
entry.Logln(FatalLevel, args...)
|
||||
entry.Logger.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
entry.Logln(PanicLevel, args...)
|
||||
}
|
||||
|
||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||
// string allocation, we do the simplest thing.
|
||||
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||
msg := fmt.Sprintln(args...)
|
||||
return msg[:len(msg)-1]
|
||||
}
|
270
vendor/github.com/ManyakRus/logrus/exported.go
generated
vendored
Normal file
270
vendor/github.com/ManyakRus/logrus/exported.go
generated
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// std is the name of the standard logger in stdlib `log`
|
||||
std = New()
|
||||
)
|
||||
|
||||
func StandardLogger() *Logger {
|
||||
return std
|
||||
}
|
||||
|
||||
// SetOutput sets the standard logger output.
|
||||
func SetOutput(out io.Writer) {
|
||||
std.SetOutput(out)
|
||||
}
|
||||
|
||||
// SetFormatter sets the standard logger formatter.
|
||||
func SetFormatter(formatter Formatter) {
|
||||
std.SetFormatter(formatter)
|
||||
}
|
||||
|
||||
// SetReportCaller sets whether the standard logger will include the calling
|
||||
// method as a field.
|
||||
func SetReportCaller(include bool) {
|
||||
std.SetReportCaller(include)
|
||||
}
|
||||
|
||||
// SetLevel sets the standard logger level.
|
||||
func SetLevel(level Level) {
|
||||
std.SetLevel(level)
|
||||
}
|
||||
|
||||
// GetLevel returns the standard logger level.
|
||||
func GetLevel() Level {
|
||||
return std.GetLevel()
|
||||
}
|
||||
|
||||
// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
|
||||
func IsLevelEnabled(level Level) bool {
|
||||
return std.IsLevelEnabled(level)
|
||||
}
|
||||
|
||||
// AddHook adds a hook to the standard logger hooks.
|
||||
func AddHook(hook Hook) {
|
||||
std.AddHook(hook)
|
||||
}
|
||||
|
||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||
func WithError(err error) *Entry {
|
||||
return std.WithField(ErrorKey, err)
|
||||
}
|
||||
|
||||
// WithContext creates an entry from the standard logger and adds a context to it.
|
||||
func WithContext(ctx context.Context) *Entry {
|
||||
return std.WithContext(ctx)
|
||||
}
|
||||
|
||||
// WithField creates an entry from the standard logger and adds a field to
|
||||
// it. If you want multiple fields, use `WithFields`.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithField(key string, value interface{}) *Entry {
|
||||
return std.WithField(key, value)
|
||||
}
|
||||
|
||||
// WithFields creates an entry from the standard logger and adds multiple
|
||||
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||
// once for each field.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithFields(fields Fields) *Entry {
|
||||
return std.WithFields(fields)
|
||||
}
|
||||
|
||||
// WithTime creates an entry from the standard logger and overrides the time of
|
||||
// logs generated with it.
|
||||
//
|
||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||
// or Panic on the Entry it returns.
|
||||
func WithTime(t time.Time) *Entry {
|
||||
return std.WithTime(t)
|
||||
}
|
||||
|
||||
// Trace logs a message at level Trace on the standard logger.
|
||||
func Trace(args ...interface{}) {
|
||||
std.Trace(args...)
|
||||
}
|
||||
|
||||
// Debug logs a message at level Debug on the standard logger.
|
||||
func Debug(args ...interface{}) {
|
||||
std.Debug(args...)
|
||||
}
|
||||
|
||||
// Print logs a message at level Info on the standard logger.
|
||||
func Print(args ...interface{}) {
|
||||
std.Print(args...)
|
||||
}
|
||||
|
||||
// Info logs a message at level Info on the standard logger.
|
||||
func Info(args ...interface{}) {
|
||||
std.Info(args...)
|
||||
}
|
||||
|
||||
// Warn logs a message at level Warn on the standard logger.
|
||||
func Warn(args ...interface{}) {
|
||||
std.Warn(args...)
|
||||
}
|
||||
|
||||
// Warning logs a message at level Warn on the standard logger.
|
||||
func Warning(args ...interface{}) {
|
||||
std.Warning(args...)
|
||||
}
|
||||
|
||||
// Error logs a message at level Error on the standard logger.
|
||||
func Error(args ...interface{}) {
|
||||
std.Error(args...)
|
||||
}
|
||||
|
||||
// Panic logs a message at level Panic on the standard logger.
|
||||
func Panic(args ...interface{}) {
|
||||
std.Panic(args...)
|
||||
}
|
||||
|
||||
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatal(args ...interface{}) {
|
||||
std.Fatal(args...)
|
||||
}
|
||||
|
||||
// TraceFn logs a message from a func at level Trace on the standard logger.
|
||||
func TraceFn(fn LogFunction) {
|
||||
std.TraceFn(fn)
|
||||
}
|
||||
|
||||
// DebugFn logs a message from a func at level Debug on the standard logger.
|
||||
func DebugFn(fn LogFunction) {
|
||||
std.DebugFn(fn)
|
||||
}
|
||||
|
||||
// PrintFn logs a message from a func at level Info on the standard logger.
|
||||
func PrintFn(fn LogFunction) {
|
||||
std.PrintFn(fn)
|
||||
}
|
||||
|
||||
// InfoFn logs a message from a func at level Info on the standard logger.
|
||||
func InfoFn(fn LogFunction) {
|
||||
std.InfoFn(fn)
|
||||
}
|
||||
|
||||
// WarnFn logs a message from a func at level Warn on the standard logger.
|
||||
func WarnFn(fn LogFunction) {
|
||||
std.WarnFn(fn)
|
||||
}
|
||||
|
||||
// WarningFn logs a message from a func at level Warn on the standard logger.
|
||||
func WarningFn(fn LogFunction) {
|
||||
std.WarningFn(fn)
|
||||
}
|
||||
|
||||
// ErrorFn logs a message from a func at level Error on the standard logger.
|
||||
func ErrorFn(fn LogFunction) {
|
||||
std.ErrorFn(fn)
|
||||
}
|
||||
|
||||
// PanicFn logs a message from a func at level Panic on the standard logger.
|
||||
func PanicFn(fn LogFunction) {
|
||||
std.PanicFn(fn)
|
||||
}
|
||||
|
||||
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func FatalFn(fn LogFunction) {
|
||||
std.FatalFn(fn)
|
||||
}
|
||||
|
||||
// Tracef logs a message at level Trace on the standard logger.
|
||||
func Tracef(format string, args ...interface{}) {
|
||||
std.Tracef(format, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message at level Debug on the standard logger.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
std.Debugf(format, args...)
|
||||
}
|
||||
|
||||
// Printf logs a message at level Info on the standard logger.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
std.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Infof logs a message at level Info on the standard logger.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
std.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warnf logs a message at level Warn on the standard logger.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
std.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Warningf logs a message at level Warn on the standard logger.
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
std.Warningf(format, args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message at level Error on the standard logger.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
std.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Panicf logs a message at level Panic on the standard logger.
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
std.Panicf(format, args...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
std.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// Traceln logs a message at level Trace on the standard logger.
|
||||
func Traceln(args ...interface{}) {
|
||||
std.Traceln(args...)
|
||||
}
|
||||
|
||||
// Debugln logs a message at level Debug on the standard logger.
|
||||
func Debugln(args ...interface{}) {
|
||||
std.Debugln(args...)
|
||||
}
|
||||
|
||||
// Println logs a message at level Info on the standard logger.
|
||||
func Println(args ...interface{}) {
|
||||
std.Println(args...)
|
||||
}
|
||||
|
||||
// Infoln logs a message at level Info on the standard logger.
|
||||
func Infoln(args ...interface{}) {
|
||||
std.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warnln logs a message at level Warn on the standard logger.
|
||||
func Warnln(args ...interface{}) {
|
||||
std.Warnln(args...)
|
||||
}
|
||||
|
||||
// Warningln logs a message at level Warn on the standard logger.
|
||||
func Warningln(args ...interface{}) {
|
||||
std.Warningln(args...)
|
||||
}
|
||||
|
||||
// Errorln logs a message at level Error on the standard logger.
|
||||
func Errorln(args ...interface{}) {
|
||||
std.Errorln(args...)
|
||||
}
|
||||
|
||||
// Panicln logs a message at level Panic on the standard logger.
|
||||
func Panicln(args ...interface{}) {
|
||||
std.Panicln(args...)
|
||||
}
|
||||
|
||||
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||
func Fatalln(args ...interface{}) {
|
||||
std.Fatalln(args...)
|
||||
}
|
78
vendor/github.com/ManyakRus/logrus/formatter.go
generated
vendored
Normal file
78
vendor/github.com/ManyakRus/logrus/formatter.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package logrus
|
||||
|
||||
import "time"
|
||||
|
||||
// Default key names for the default fields
|
||||
const (
|
||||
defaultTimestampFormat = time.RFC3339
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
FieldKeyLogrusError = "logrus_error"
|
||||
FieldKeyFunc = "func"
|
||||
FieldKeyFile = "file"
|
||||
)
|
||||
|
||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||
// `Entry`. It exposes all the fields, including the default ones:
|
||||
//
|
||||
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||
// * `entry.Data["time"]`. The timestamp.
|
||||
// * `entry.Data["level"]. The level the entry was logged at.
|
||||
//
|
||||
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||
// logged to `logger.Out`.
|
||||
type Formatter interface {
|
||||
Format(*Entry) ([]byte, error)
|
||||
}
|
||||
|
||||
// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
|
||||
// dumping it. If this code wasn't there doing:
|
||||
//
|
||||
// logrus.WithField("level", 1).Info("hello")
|
||||
//
|
||||
// Would just silently drop the user provided level. Instead with this code
|
||||
// it'll logged as:
|
||||
//
|
||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||
//
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) {
|
||||
timeKey := fieldMap.resolve(FieldKeyTime)
|
||||
if t, ok := data[timeKey]; ok {
|
||||
data["fields."+timeKey] = t
|
||||
delete(data, timeKey)
|
||||
}
|
||||
|
||||
msgKey := fieldMap.resolve(FieldKeyMsg)
|
||||
if m, ok := data[msgKey]; ok {
|
||||
data["fields."+msgKey] = m
|
||||
delete(data, msgKey)
|
||||
}
|
||||
|
||||
levelKey := fieldMap.resolve(FieldKeyLevel)
|
||||
if l, ok := data[levelKey]; ok {
|
||||
data["fields."+levelKey] = l
|
||||
delete(data, levelKey)
|
||||
}
|
||||
|
||||
logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
|
||||
if l, ok := data[logrusErrKey]; ok {
|
||||
data["fields."+logrusErrKey] = l
|
||||
delete(data, logrusErrKey)
|
||||
}
|
||||
|
||||
// If reportCaller is not set, 'func' will not conflict.
|
||||
if reportCaller {
|
||||
funcKey := fieldMap.resolve(FieldKeyFunc)
|
||||
if l, ok := data[funcKey]; ok {
|
||||
data["fields."+funcKey] = l
|
||||
}
|
||||
fileKey := fieldMap.resolve(FieldKeyFile)
|
||||
if l, ok := data[fileKey]; ok {
|
||||
data["fields."+fileKey] = l
|
||||
}
|
||||
}
|
||||
}
|
34
vendor/github.com/ManyakRus/logrus/hooks.go
generated
vendored
Normal file
34
vendor/github.com/ManyakRus/logrus/hooks.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
package logrus
|
||||
|
||||
// A hook to be fired when logging on the logging levels returned from
|
||||
// `Levels()` on your implementation of the interface. Note that this is not
|
||||
// fired in a goroutine or a channel with workers, you should handle such
|
||||
// functionality yourself if your call is non-blocking and you don't wish for
|
||||
// the logging calls for levels returned from `Levels()` to block.
|
||||
type Hook interface {
|
||||
Levels() []Level
|
||||
Fire(*Entry) error
|
||||
}
|
||||
|
||||
// Internal type for storing the hooks on a logger instance.
|
||||
type LevelHooks map[Level][]Hook
|
||||
|
||||
// Add a hook to an instance of logger. This is called with
|
||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||
func (hooks LevelHooks) Add(hook Hook) {
|
||||
for _, level := range hook.Levels() {
|
||||
hooks[level] = append(hooks[level], hook)
|
||||
}
|
||||
}
|
||||
|
||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||
// appropriate hooks for a log entry.
|
||||
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||
for _, hook := range hooks[level] {
|
||||
if err := hook.Fire(entry); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
128
vendor/github.com/ManyakRus/logrus/json_formatter.go
generated
vendored
Normal file
128
vendor/github.com/ManyakRus/logrus/json_formatter.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
|
||||
// FieldMap allows customization of the key names for default fields.
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
}
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
// JSONFormatter formats logs into parsable json
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||
// library.
|
||||
// The standard Library already provides a set of predefined format.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
DisableTimestamp bool
|
||||
|
||||
// DisableHTMLEscape allows disabling html escaping in output
|
||||
DisableHTMLEscape bool
|
||||
|
||||
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
||||
DataKey string
|
||||
|
||||
// FieldMap allows users to customize the names of keys for default fields.
|
||||
// As an example:
|
||||
// formatter := &JSONFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyMsg: "@message",
|
||||
// FieldKeyFunc: "@caller",
|
||||
// },
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
|
||||
// CallerPrettyfier can be set by the user to modify the content
|
||||
// of the function and file keys in the json data when ReportCaller is
|
||||
// activated. If any of the returned value is the empty string the
|
||||
// corresponding key will be removed from json fields.
|
||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||
|
||||
// PrettyPrint will indent all json logs
|
||||
PrettyPrint bool
|
||||
}
|
||||
|
||||
// Format renders a single log entry
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
data := make(Fields, len(entry.Data)+4)
|
||||
for k, v := range entry.Data {
|
||||
switch v := v.(type) {
|
||||
case error:
|
||||
// Otherwise errors are ignored by `encoding/json`
|
||||
// https://github.com/sirupsen/logrus/issues/137
|
||||
data[k] = v.Error()
|
||||
default:
|
||||
data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if f.DataKey != "" {
|
||||
newData := make(Fields, 4)
|
||||
newData[f.DataKey] = data
|
||||
data = newData
|
||||
}
|
||||
|
||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
||||
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
|
||||
if entry.err != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
||||
}
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
if entry.HasCaller() {
|
||||
funcVal := entry.Caller.Function
|
||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
}
|
||||
if funcVal != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
||||
}
|
||||
if fileVal != "" {
|
||||
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
||||
}
|
||||
}
|
||||
|
||||
var b *bytes.Buffer
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(b)
|
||||
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
||||
if f.PrettyPrint {
|
||||
encoder.SetIndent("", " ")
|
||||
}
|
||||
if err := encoder.Encode(data); err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
417
vendor/github.com/ManyakRus/logrus/logger.go
generated
vendored
Normal file
417
vendor/github.com/ManyakRus/logrus/logger.go
generated
vendored
Normal file
@ -0,0 +1,417 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogFunction For big messages, it can be more efficient to pass a function
|
||||
// and only call it if the log level is actually enables rather than
|
||||
// generating the log message and then checking if the level is enabled
|
||||
type LogFunction func() []interface{}
|
||||
|
||||
type Logger struct {
|
||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||
// something more adventurous, such as logging to Kafka.
|
||||
Out io.Writer
|
||||
// Hooks for the logger instance. These allow firing events based on logging
|
||||
// levels and log entries. For example, to send errors to an error tracking
|
||||
// service, log to StatsD or dump the core on fatal errors.
|
||||
Hooks LevelHooks
|
||||
// All log entries pass through the formatter before logged to Out. The
|
||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||
// own that implements the `Formatter` interface, see the `README` or included
|
||||
// formatters for examples.
|
||||
Formatter Formatter
|
||||
|
||||
// Flag for whether to log caller info (off by default)
|
||||
ReportCaller bool
|
||||
|
||||
// The logging level the logger should log at. This is typically (and defaults
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged.
|
||||
Level Level
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
// Function to exit the application, defaults to `os.Exit()`
|
||||
ExitFunc exitFunc
|
||||
// The buffer pool used to format the log. If it is nil, the default global
|
||||
// buffer pool will be used.
|
||||
BufferPool BufferPool
|
||||
}
|
||||
|
||||
type exitFunc func(int)
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
||||
// instantiate your own:
|
||||
//
|
||||
// var log = &logrus.Logger{
|
||||
// Out: os.Stderr,
|
||||
// Formatter: new(logrus.TextFormatter),
|
||||
// Hooks: make(logrus.LevelHooks),
|
||||
// Level: logrus.DebugLevel,
|
||||
// }
|
||||
//
|
||||
// It's recommended to make this a global instance called `log`.
|
||||
func New() *Logger {
|
||||
return &Logger{
|
||||
Out: os.Stderr,
|
||||
Formatter: new(TextFormatter),
|
||||
Hooks: make(LevelHooks),
|
||||
Level: InfoLevel,
|
||||
ExitFunc: os.Exit,
|
||||
ReportCaller: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
entry.Data = map[string]interface{}{}
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// WithField allocates a new entry and adds a field to it.
|
||||
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
||||
// this new returned entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
// Add a context to the log entry.
|
||||
func (logger *Logger) WithContext(ctx context.Context) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithContext(ctx)
|
||||
}
|
||||
|
||||
// Overrides the time of the log entry.
|
||||
func (logger *Logger) WithTime(t time.Time) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithTime(t)
|
||||
}
|
||||
|
||||
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Logf(level, format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
||||
logger.Logf(TraceLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
logger.Logf(DebugLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
logger.Logf(InfoLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
logger.Logf(WarnLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
logger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
logger.Logf(ErrorLevel, format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
logger.Logf(FatalLevel, format, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
logger.Logf(PanicLevel, format, args...)
|
||||
}
|
||||
|
||||
// Log will log a message at the level given as parameter.
|
||||
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
||||
func (logger *Logger) Log(level Level, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Log(level, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) LogFn(level Level, fn LogFunction) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Log(level, fn()...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Trace(args ...interface{}) {
|
||||
logger.Log(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
logger.Log(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
logger.Log(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Print(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
logger.Log(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
logger.Warn(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
logger.Log(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
logger.Log(FatalLevel, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
logger.Log(PanicLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) TraceFn(fn LogFunction) {
|
||||
logger.LogFn(TraceLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) DebugFn(fn LogFunction) {
|
||||
logger.LogFn(DebugLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) InfoFn(fn LogFunction) {
|
||||
logger.LogFn(InfoLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) PrintFn(fn LogFunction) {
|
||||
entry := logger.newEntry()
|
||||
entry.Print(fn()...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) WarnFn(fn LogFunction) {
|
||||
logger.LogFn(WarnLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) WarningFn(fn LogFunction) {
|
||||
logger.WarnFn(fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) ErrorFn(fn LogFunction) {
|
||||
logger.LogFn(ErrorLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) FatalFn(fn LogFunction) {
|
||||
logger.LogFn(FatalLevel, fn)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) PanicFn(fn LogFunction) {
|
||||
logger.LogFn(PanicLevel, fn)
|
||||
}
|
||||
|
||||
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
||||
if logger.IsLevelEnabled(level) {
|
||||
entry := logger.newEntry()
|
||||
entry.Logln(level, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Traceln(args ...interface{}) {
|
||||
logger.Logln(TraceLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
logger.Logln(DebugLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
logger.Logln(InfoLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
logger.Logln(WarnLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
logger.Warnln(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
logger.Logln(ErrorLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
logger.Logln(FatalLevel, args...)
|
||||
logger.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
logger.Logln(PanicLevel, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Exit(code int) {
|
||||
runHandlers()
|
||||
if logger.ExitFunc == nil {
|
||||
logger.ExitFunc = os.Exit
|
||||
}
|
||||
logger.ExitFunc(code)
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
||||
func (logger *Logger) level() Level {
|
||||
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
||||
}
|
||||
|
||||
// SetLevel sets the logger level.
|
||||
func (logger *Logger) SetLevel(level Level) {
|
||||
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
||||
}
|
||||
|
||||
// GetLevel returns the logger level.
|
||||
func (logger *Logger) GetLevel() Level {
|
||||
return logger.level()
|
||||
}
|
||||
|
||||
// AddHook adds a hook to the logger hooks.
|
||||
func (logger *Logger) AddHook(hook Hook) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
logger.Hooks.Add(hook)
|
||||
}
|
||||
|
||||
// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
||||
func (logger *Logger) IsLevelEnabled(level Level) bool {
|
||||
return logger.level() >= level
|
||||
}
|
||||
|
||||
// SetFormatter sets the logger formatter.
|
||||
func (logger *Logger) SetFormatter(formatter Formatter) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
logger.Formatter = formatter
|
||||
}
|
||||
|
||||
// SetOutput sets the logger output.
|
||||
func (logger *Logger) SetOutput(output io.Writer) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
logger.Out = output
|
||||
}
|
||||
|
||||
func (logger *Logger) SetReportCaller(reportCaller bool) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
logger.ReportCaller = reportCaller
|
||||
}
|
||||
|
||||
// ReplaceHooks replaces the logger hooks and returns the old ones
|
||||
func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
|
||||
logger.mu.Lock()
|
||||
oldHooks := logger.Hooks
|
||||
logger.Hooks = hooks
|
||||
logger.mu.Unlock()
|
||||
return oldHooks
|
||||
}
|
||||
|
||||
// SetBufferPool sets the logger buffer pool.
|
||||
func (logger *Logger) SetBufferPool(pool BufferPool) {
|
||||
logger.mu.Lock()
|
||||
defer logger.mu.Unlock()
|
||||
logger.BufferPool = pool
|
||||
}
|
186
vendor/github.com/ManyakRus/logrus/logrus.go
generated
vendored
Normal file
186
vendor/github.com/ManyakRus/logrus/logrus.go
generated
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// Level type
|
||||
type Level uint32
|
||||
|
||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
||||
func (level Level) String() string {
|
||||
if b, err := level.MarshalText(); err == nil {
|
||||
return string(b)
|
||||
} else {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
func ParseLevel(lvl string) (Level, error) {
|
||||
switch strings.ToLower(lvl) {
|
||||
case "panic":
|
||||
return PanicLevel, nil
|
||||
case "fatal":
|
||||
return FatalLevel, nil
|
||||
case "error":
|
||||
return ErrorLevel, nil
|
||||
case "warn", "warning":
|
||||
return WarnLevel, nil
|
||||
case "info":
|
||||
return InfoLevel, nil
|
||||
case "debug":
|
||||
return DebugLevel, nil
|
||||
case "trace":
|
||||
return TraceLevel, nil
|
||||
}
|
||||
|
||||
var l Level
|
||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (level *Level) UnmarshalText(text []byte) error {
|
||||
l, err := ParseLevel(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*level = l
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (level Level) MarshalText() ([]byte, error) {
|
||||
switch level {
|
||||
case TraceLevel:
|
||||
return []byte("trace"), nil
|
||||
case DebugLevel:
|
||||
return []byte("debug"), nil
|
||||
case InfoLevel:
|
||||
return []byte("info"), nil
|
||||
case WarnLevel:
|
||||
return []byte("warning"), nil
|
||||
case ErrorLevel:
|
||||
return []byte("error"), nil
|
||||
case FatalLevel:
|
||||
return []byte("fatal"), nil
|
||||
case PanicLevel:
|
||||
return []byte("panic"), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not a valid logrus level %d", level)
|
||||
}
|
||||
|
||||
// A constant exposing all logging levels
|
||||
var AllLevels = []Level{
|
||||
PanicLevel,
|
||||
FatalLevel,
|
||||
ErrorLevel,
|
||||
WarnLevel,
|
||||
InfoLevel,
|
||||
DebugLevel,
|
||||
TraceLevel,
|
||||
}
|
||||
|
||||
// These are the different logging levels. You can set the logging level to log
|
||||
// on your instance of logger, obtained with `logrus.New()`.
|
||||
const (
|
||||
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||
// message passed to Debug, Info, ...
|
||||
PanicLevel Level = iota
|
||||
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
|
||||
// logging level is set to Panic.
|
||||
FatalLevel
|
||||
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||
// Commonly used for hooks to send errors to an error tracking service.
|
||||
ErrorLevel
|
||||
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||
WarnLevel
|
||||
// InfoLevel level. General operational entries about what's going on inside the
|
||||
// application.
|
||||
InfoLevel
|
||||
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||
DebugLevel
|
||||
// TraceLevel level. Designates finer-grained informational events than the Debug.
|
||||
TraceLevel
|
||||
)
|
||||
|
||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||
var (
|
||||
_ StdLogger = &log.Logger{}
|
||||
_ StdLogger = &Entry{}
|
||||
_ StdLogger = &Logger{}
|
||||
)
|
||||
|
||||
// StdLogger is what your logrus-enabled library should take, that way
|
||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
||||
// interface, this is the closest we get, unfortunately.
|
||||
type StdLogger interface {
|
||||
Print(...interface{})
|
||||
Printf(string, ...interface{})
|
||||
Println(...interface{})
|
||||
|
||||
Fatal(...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
Fatalln(...interface{})
|
||||
|
||||
Panic(...interface{})
|
||||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
}
|
||||
|
||||
// The FieldLogger interface generalizes the Entry and Logger types
|
||||
type FieldLogger interface {
|
||||
WithField(key string, value interface{}) *Entry
|
||||
WithFields(fields Fields) *Entry
|
||||
WithError(err error) *Entry
|
||||
|
||||
Debugf(format string, args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Warningf(format string, args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
|
||||
Debug(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Warn(args ...interface{})
|
||||
Warning(args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Fatal(args ...interface{})
|
||||
Panic(args ...interface{})
|
||||
|
||||
Debugln(args ...interface{})
|
||||
Infoln(args ...interface{})
|
||||
Println(args ...interface{})
|
||||
Warnln(args ...interface{})
|
||||
Warningln(args ...interface{})
|
||||
Errorln(args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Panicln(args ...interface{})
|
||||
|
||||
// IsDebugEnabled() bool
|
||||
// IsInfoEnabled() bool
|
||||
// IsWarnEnabled() bool
|
||||
// IsErrorEnabled() bool
|
||||
// IsFatalEnabled() bool
|
||||
// IsPanicEnabled() bool
|
||||
}
|
||||
|
||||
// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
|
||||
// here for consistancy. Do not use. Use Logger or Entry instead.
|
||||
type Ext1FieldLogger interface {
|
||||
FieldLogger
|
||||
Tracef(format string, args ...interface{})
|
||||
Trace(args ...interface{})
|
||||
Traceln(args ...interface{})
|
||||
}
|
11
vendor/github.com/ManyakRus/logrus/terminal_check_appengine.go
generated
vendored
Normal file
11
vendor/github.com/ManyakRus/logrus/terminal_check_appengine.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
return true
|
||||
}
|
13
vendor/github.com/ManyakRus/logrus/terminal_check_bsd.go
generated
vendored
Normal file
13
vendor/github.com/ManyakRus/logrus/terminal_check_bsd.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
// +build !js
|
||||
|
||||
package logrus
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
7
vendor/github.com/ManyakRus/logrus/terminal_check_js.go
generated
vendored
Normal file
7
vendor/github.com/ManyakRus/logrus/terminal_check_js.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// +build js
|
||||
|
||||
package logrus
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
return false
|
||||
}
|
11
vendor/github.com/ManyakRus/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
11
vendor/github.com/ManyakRus/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// +build js nacl plan9
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
return false
|
||||
}
|
17
vendor/github.com/ManyakRus/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
17
vendor/github.com/ManyakRus/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// +build !appengine,!js,!windows,!nacl,!plan9
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
return isTerminal(int(v.Fd()))
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
11
vendor/github.com/ManyakRus/logrus/terminal_check_solaris.go
generated
vendored
Normal file
11
vendor/github.com/ManyakRus/logrus/terminal_check_solaris.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
13
vendor/github.com/ManyakRus/logrus/terminal_check_unix.go
generated
vendored
Normal file
13
vendor/github.com/ManyakRus/logrus/terminal_check_unix.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// +build linux aix zos
|
||||
// +build !js
|
||||
|
||||
package logrus
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
|
||||
func isTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
27
vendor/github.com/ManyakRus/logrus/terminal_check_windows.go
generated
vendored
Normal file
27
vendor/github.com/ManyakRus/logrus/terminal_check_windows.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
// +build !appengine,!js,windows
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func checkIfTerminal(w io.Writer) bool {
|
||||
switch v := w.(type) {
|
||||
case *os.File:
|
||||
handle := windows.Handle(v.Fd())
|
||||
var mode uint32
|
||||
if err := windows.GetConsoleMode(handle, &mode); err != nil {
|
||||
return false
|
||||
}
|
||||
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
if err := windows.SetConsoleMode(handle, mode); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
339
vendor/github.com/ManyakRus/logrus/text_formatter.go
generated
vendored
Normal file
339
vendor/github.com/ManyakRus/logrus/text_formatter.go
generated
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
red = 31
|
||||
yellow = 33
|
||||
blue = 36
|
||||
gray = 37
|
||||
)
|
||||
|
||||
var baseTimestamp time.Time
|
||||
|
||||
func init() {
|
||||
baseTimestamp = time.Now()
|
||||
}
|
||||
|
||||
// TextFormatter formats logs into text
|
||||
type TextFormatter struct {
|
||||
// Set to true to bypass checking for a TTY before outputting colors.
|
||||
ForceColors bool
|
||||
|
||||
// Force disabling colors.
|
||||
DisableColors bool
|
||||
|
||||
// Force quoting of all values
|
||||
ForceQuote bool
|
||||
|
||||
// DisableQuote disables quoting for all values.
|
||||
// DisableQuote will have a lower priority than ForceQuote.
|
||||
// If both of them are set to true, quote will be forced on all values.
|
||||
DisableQuote bool
|
||||
|
||||
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
||||
EnvironmentOverrideColors bool
|
||||
|
||||
// Disable timestamp logging. useful when output is redirected to logging
|
||||
// system that already adds timestamps.
|
||||
DisableTimestamp bool
|
||||
|
||||
// Enable logging the full timestamp when a TTY is attached instead of just
|
||||
// the time passed since beginning of execution.
|
||||
FullTimestamp bool
|
||||
|
||||
// TimestampFormat to use for display when a full timestamp is printed.
|
||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||
// library.
|
||||
// The standard Library already provides a set of predefined format.
|
||||
TimestampFormat string
|
||||
|
||||
// The fields are sorted by default for a consistent output. For applications
|
||||
// that log extremely frequently and don't use the JSON formatter this may not
|
||||
// be desired.
|
||||
DisableSorting bool
|
||||
|
||||
// The keys sorting function, when uninitialized it uses sort.Strings.
|
||||
SortingFunc func([]string)
|
||||
|
||||
// Disables the truncation of the level text to 4 characters.
|
||||
DisableLevelTruncation bool
|
||||
|
||||
// PadLevelText Adds padding the level text so that all the levels output at the same length
|
||||
// PadLevelText is a superset of the DisableLevelTruncation option
|
||||
PadLevelText bool
|
||||
|
||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||
QuoteEmptyFields bool
|
||||
|
||||
// Whether the logger's out is to a terminal
|
||||
isTerminal bool
|
||||
|
||||
// FieldMap allows users to customize the names of keys for default fields.
|
||||
// As an example:
|
||||
// formatter := &TextFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyMsg: "@message"}}
|
||||
FieldMap FieldMap
|
||||
|
||||
// CallerPrettyfier can be set by the user to modify the content
|
||||
// of the function and file keys in the data when ReportCaller is
|
||||
// activated. If any of the returned value is the empty string the
|
||||
// corresponding key will be removed from fields.
|
||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||
|
||||
terminalInitOnce sync.Once
|
||||
|
||||
// The max length of the level text, generated dynamically on init
|
||||
levelTextMaxLength int
|
||||
}
|
||||
|
||||
func (f *TextFormatter) init(entry *Entry) {
|
||||
if entry.Logger != nil {
|
||||
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||
}
|
||||
// Get the max length of the level text
|
||||
for _, level := range AllLevels {
|
||||
levelTextLength := utf8.RuneCount([]byte(level.String()))
|
||||
if levelTextLength > f.levelTextMaxLength {
|
||||
f.levelTextMaxLength = levelTextLength
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) isColored() bool {
|
||||
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
||||
|
||||
if f.EnvironmentOverrideColors {
|
||||
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
|
||||
case ok && force != "0":
|
||||
isColored = true
|
||||
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
|
||||
isColored = false
|
||||
}
|
||||
}
|
||||
|
||||
return isColored && !f.DisableColors
|
||||
}
|
||||
|
||||
// Format renders a single log entry
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
data := make(Fields)
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
||||
keys := make([]string, 0, len(data))
|
||||
for k := range data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
var funcVal, fileVal string
|
||||
|
||||
fixedKeys := make([]string, 0, 4+len(data))
|
||||
if !f.DisableTimestamp {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
||||
}
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
|
||||
if entry.Message != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
|
||||
}
|
||||
if entry.err != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
||||
}
|
||||
if entry.HasCaller() {
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
} else {
|
||||
funcVal = entry.Caller.Function
|
||||
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
}
|
||||
|
||||
if funcVal != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
|
||||
}
|
||||
if fileVal != "" {
|
||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
|
||||
}
|
||||
}
|
||||
|
||||
if !f.DisableSorting {
|
||||
if f.SortingFunc == nil {
|
||||
sort.Strings(keys)
|
||||
fixedKeys = append(fixedKeys, keys...)
|
||||
} else {
|
||||
if !f.isColored() {
|
||||
fixedKeys = append(fixedKeys, keys...)
|
||||
f.SortingFunc(fixedKeys)
|
||||
} else {
|
||||
f.SortingFunc(keys)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fixedKeys = append(fixedKeys, keys...)
|
||||
}
|
||||
|
||||
var b *bytes.Buffer
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
f.terminalInitOnce.Do(func() { f.init(entry) })
|
||||
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
timestampFormat = defaultTimestampFormat
|
||||
}
|
||||
if f.isColored() {
|
||||
f.printColored(b, entry, keys, data, timestampFormat)
|
||||
} else {
|
||||
|
||||
for _, key := range fixedKeys {
|
||||
var value interface{}
|
||||
switch {
|
||||
case key == f.FieldMap.resolve(FieldKeyTime):
|
||||
value = entry.Time.Format(timestampFormat)
|
||||
case key == f.FieldMap.resolve(FieldKeyLevel):
|
||||
value = entry.Level.String()
|
||||
case key == f.FieldMap.resolve(FieldKeyMsg):
|
||||
value = entry.Message
|
||||
case key == f.FieldMap.resolve(FieldKeyLogrusError):
|
||||
value = entry.err
|
||||
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
|
||||
value = funcVal
|
||||
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
|
||||
value = fileVal
|
||||
default:
|
||||
value = data[key]
|
||||
}
|
||||
f.appendKeyValue(b, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteByte('\n')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
|
||||
var levelColor int
|
||||
switch entry.Level {
|
||||
case DebugLevel, TraceLevel:
|
||||
levelColor = gray
|
||||
case WarnLevel:
|
||||
levelColor = yellow
|
||||
case ErrorLevel, FatalLevel, PanicLevel:
|
||||
levelColor = red
|
||||
case InfoLevel:
|
||||
levelColor = blue
|
||||
default:
|
||||
levelColor = blue
|
||||
}
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())
|
||||
if !f.DisableLevelTruncation && !f.PadLevelText {
|
||||
levelText = levelText[0:4]
|
||||
}
|
||||
if f.PadLevelText {
|
||||
// Generates the format string used in the next line, for example "%-6s" or "%-7s".
|
||||
// Based on the max level text length.
|
||||
formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
|
||||
// Formats the level text by appending spaces up to the max length, for example:
|
||||
// - "INFO "
|
||||
// - "WARNING"
|
||||
levelText = fmt.Sprintf(formatString, levelText)
|
||||
}
|
||||
|
||||
// Remove a single newline if it already exists in the message to keep
|
||||
// the behavior of logrus text_formatter the same as the stdlib log package
|
||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
||||
|
||||
caller := ""
|
||||
if entry.HasCaller() {
|
||||
funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
|
||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||
|
||||
if f.CallerPrettyfier != nil {
|
||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||
}
|
||||
|
||||
if fileVal == "" {
|
||||
caller = funcVal
|
||||
} else if funcVal == "" {
|
||||
caller = fileVal
|
||||
} else {
|
||||
caller = fileVal + " " + funcVal
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case f.DisableTimestamp:
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
||||
case !f.FullTimestamp:
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
||||
default:
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||
if f.ForceQuote {
|
||||
return true
|
||||
}
|
||||
if f.QuoteEmptyFields && len(text) == 0 {
|
||||
return true
|
||||
}
|
||||
if f.DisableQuote {
|
||||
return false
|
||||
}
|
||||
for _, ch := range text {
|
||||
if !((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
if b.Len() > 0 {
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
stringVal, ok := value.(string)
|
||||
if !ok {
|
||||
stringVal = fmt.Sprint(value)
|
||||
}
|
||||
|
||||
if !f.needsQuoting(stringVal) {
|
||||
b.WriteString(stringVal)
|
||||
} else {
|
||||
b.WriteString(fmt.Sprintf("%q", stringVal))
|
||||
}
|
||||
}
|
70
vendor/github.com/ManyakRus/logrus/writer.go
generated
vendored
Normal file
70
vendor/github.com/ManyakRus/logrus/writer.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Writer at INFO level. See WriterLevel for details.
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
// WriterLevel returns an io.Writer that can be used to write arbitrary text to
|
||||
// the logger at the given log level. Each line written to the writer will be
|
||||
// printed in the usual way using formatters and hooks. The writer is part of an
|
||||
// io.Pipe and it is the callers responsibility to close the writer when done.
|
||||
// This can be used to override the standard library logger easily.
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
return NewEntry(logger).WriterLevel(level)
|
||||
}
|
||||
|
||||
func (entry *Entry) Writer() *io.PipeWriter {
|
||||
return entry.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
var printFunc func(args ...interface{})
|
||||
|
||||
switch level {
|
||||
case TraceLevel:
|
||||
printFunc = entry.Trace
|
||||
case DebugLevel:
|
||||
printFunc = entry.Debug
|
||||
case InfoLevel:
|
||||
printFunc = entry.Info
|
||||
case WarnLevel:
|
||||
printFunc = entry.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = entry.Error
|
||||
case FatalLevel:
|
||||
printFunc = entry.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = entry.Panic
|
||||
default:
|
||||
printFunc = entry.Print
|
||||
}
|
||||
|
||||
go entry.writerScanner(reader, printFunc)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
printFunc(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
entry.Errorf("Error while reading from Writer: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
}
|
||||
|
||||
func writerFinalizer(writer *io.PipeWriter) {
|
||||
writer.Close()
|
||||
}
|
44
vendor/github.com/ManyakRus/starter/config/config.go
generated
vendored
Normal file
44
vendor/github.com/ManyakRus/starter/config/config.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// модуль для загрузки переменных окружения в структуру
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
//log "github.com/sirupsen/logrus"
|
||||
//log "github.com/sirupsen/logrus"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/types"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/micro"
|
||||
)
|
||||
|
||||
// log хранит используемый логгер
|
||||
var log = logger.GetLog()
|
||||
|
||||
// LoadEnv - загружает переменные окружения в структуру из файла или из переменных окружения
|
||||
func LoadEnv() {
|
||||
|
||||
dir := micro.ProgramDir()
|
||||
filename := dir + ".env"
|
||||
LoadEnv_from_file(filename)
|
||||
}
|
||||
|
||||
// LoadEnv_from_file загружает переменные окружения в структуру из файла или из переменных окружения
|
||||
func LoadEnv_from_file(filename string) {
|
||||
|
||||
err := godotenv.Load(filename)
|
||||
if err != nil {
|
||||
log.Debug("Can not parse .env file: ", filename, " error: "+err.Error())
|
||||
} else {
|
||||
log.Info("load .env from file: ", filename)
|
||||
}
|
||||
|
||||
LOG_LEVEL := os.Getenv("LOG_LEVEL")
|
||||
if LOG_LEVEL == "" {
|
||||
LOG_LEVEL = "info"
|
||||
}
|
||||
logger.SetLevel(LOG_LEVEL)
|
||||
|
||||
}
|
52
vendor/github.com/ManyakRus/starter/contextmain/contextmain.go
generated
vendored
Normal file
52
vendor/github.com/ManyakRus/starter/contextmain/contextmain.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// модуль для получения единого Context приложения
|
||||
|
||||
package contextmain
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Ctx хранит глобальный контекст программы
|
||||
// не использовать
|
||||
// * - чтоб можно было засунуть ссылку на чужой контекст
|
||||
var Ctx *context.Context
|
||||
|
||||
// CancelContext - функция отмены глобального контекста
|
||||
var CancelContext func()
|
||||
|
||||
// onceCtx - гарантирует единственное создание контеста
|
||||
var onceCtx sync.Once
|
||||
|
||||
// lockContextMain - гарантирует единственное создание контеста
|
||||
// var lockContextMain sync.Mutex
|
||||
|
||||
// GetContext - возвращает глобальный контекст приложения
|
||||
func GetContext() context.Context {
|
||||
//lockContextMain.Lock()
|
||||
//defer lockContextMain.Unlock()
|
||||
//
|
||||
//if Ctx == nil {
|
||||
// CtxBg := context.Background()
|
||||
// Ctx, CancelContext = context.WithCancel(CtxBg)
|
||||
//}
|
||||
|
||||
onceCtx.Do(func() {
|
||||
CtxBg := context.Background()
|
||||
var Ctx0 context.Context
|
||||
Ctx0, CancelContext = context.WithCancel(CtxBg)
|
||||
Ctx = &Ctx0
|
||||
})
|
||||
|
||||
return *Ctx
|
||||
}
|
||||
|
||||
// GetNewContext - создаёт и возвращает новый контекст приложения
|
||||
func GetNewContext() context.Context {
|
||||
CtxBg := context.Background()
|
||||
var Ctx0 context.Context
|
||||
Ctx0, CancelContext = context.WithCancel(CtxBg)
|
||||
Ctx = &Ctx0
|
||||
|
||||
return *Ctx
|
||||
}
|
89
vendor/github.com/ManyakRus/starter/folders/folders.go
generated
vendored
Normal file
89
vendor/github.com/ManyakRus/starter/folders/folders.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
package folders
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type Folder struct {
|
||||
FileName string
|
||||
Name string
|
||||
Files []*File
|
||||
Folders map[string]*Folder
|
||||
}
|
||||
|
||||
func (f *Folder) String() string {
|
||||
j, _ := json.Marshal(f)
|
||||
return string(j)
|
||||
}
|
||||
|
||||
// FindFoldersTree - возвращает дерево каталогов и файлов, начиная с директории dir
|
||||
func FindFoldersTree(dir string, NeedFolders, NeedFiles, NeedDot bool, exclude string) *Folder {
|
||||
dir = path.Clean(dir)
|
||||
var tree *Folder
|
||||
var nodes = map[string]interface{}{}
|
||||
var walkFun filepath.WalkFunc = func(p string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
nodes[p] = &Folder{p, path.Base(p), []*File{}, map[string]*Folder{}}
|
||||
} else {
|
||||
nodes[p] = &File{path.Base(p)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
err := filepath.Walk(dir, walkFun)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for key, value := range nodes {
|
||||
var parentFolder *Folder
|
||||
if key == dir {
|
||||
tree = value.(*Folder)
|
||||
continue
|
||||
} else {
|
||||
parentFolder = nodes[path.Dir(key)].(*Folder)
|
||||
}
|
||||
|
||||
// найдём название Папки/Файла
|
||||
var Name string
|
||||
switch value.(type) {
|
||||
case *File:
|
||||
Name = value.(*File).Name
|
||||
case *Folder:
|
||||
Name = value.(*Folder).Name
|
||||
}
|
||||
|
||||
// проверка скрытые файлы с точкой
|
||||
if NeedDot == false && len(Name) > 0 && Name[0:1] == "." {
|
||||
continue
|
||||
}
|
||||
|
||||
// проверка кроме exclude
|
||||
if exclude != "" && len(Name) >= len(exclude) && Name[0:len(exclude)] == exclude {
|
||||
continue
|
||||
}
|
||||
|
||||
//
|
||||
switch v := value.(type) {
|
||||
case *File:
|
||||
if NeedFiles == false {
|
||||
break
|
||||
}
|
||||
parentFolder.Files = append(parentFolder.Files, v)
|
||||
case *Folder:
|
||||
if NeedFolders == false {
|
||||
break
|
||||
}
|
||||
parentFolder.Folders[v.Name] = v
|
||||
}
|
||||
}
|
||||
|
||||
return tree
|
||||
}
|
22
vendor/github.com/ManyakRus/starter/log/logger.go
generated
vendored
Normal file
22
vendor/github.com/ManyakRus/starter/log/logger.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// модуль создания единого логирования
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
//"github.com/google/logger"
|
||||
//"github.com/sirupsen/logrus"
|
||||
logrus "github.com/ManyakRus/logrus"
|
||||
)
|
||||
|
||||
// GetLog - возвращает глобальный логгер приложения
|
||||
// и создаёт логгер если ещё не создан
|
||||
func GetLog() *logrus.Logger {
|
||||
|
||||
return logger.GetLog()
|
||||
}
|
||||
|
||||
// SetLevel - изменяет уровень логирования
|
||||
func SetLevel(LOG_LEVEL string) {
|
||||
logger.SetLevel(LOG_LEVEL)
|
||||
}
|
259
vendor/github.com/ManyakRus/starter/log/logger_proxy.go
generated
vendored
Normal file
259
vendor/github.com/ManyakRus/starter/log/logger_proxy.go
generated
vendored
Normal file
@ -0,0 +1,259 @@
|
||||
// дублирует все функции логгера logrus
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/ManyakRus/logrus"
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WithField allocates a new entry and adds a field to it.
|
||||
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
||||
// this new returned entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func WithField(key string, value interface{}) *logrus.Entry {
|
||||
return logger.GetLog().WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func WithFields(fields logrus.Fields) *logrus.Entry {
|
||||
return GetLog().WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func WithError(err error) *logrus.Entry {
|
||||
return GetLog().WithError(err)
|
||||
}
|
||||
|
||||
// Add a context to the log entry.
|
||||
func WithContext(ctx context.Context) *logrus.Entry {
|
||||
return GetLog().WithContext(ctx)
|
||||
}
|
||||
|
||||
// Overrides the time of the log entry.
|
||||
func WithTime(t time.Time) *logrus.Entry {
|
||||
|
||||
return GetLog().WithTime(t)
|
||||
}
|
||||
|
||||
func Logf(level logrus.Level, format string, args ...interface{}) {
|
||||
GetLog().Logf(level, format, args...)
|
||||
}
|
||||
|
||||
func Tracef(format string, args ...interface{}) {
|
||||
GetLog().Tracef(format, args...)
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
GetLog().Debugf(format, args...)
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
GetLog().Infof(format, args...)
|
||||
}
|
||||
|
||||
func Printf(format string, args ...interface{}) {
|
||||
GetLog().Printf(format, args...)
|
||||
}
|
||||
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
GetLog().Warnf(format, args...)
|
||||
}
|
||||
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
GetLog().Warningf(format, args...)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
GetLog().Errorf(format, args...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
GetLog().Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
GetLog().Panicf(format, args...)
|
||||
}
|
||||
|
||||
// Log will log a message at the level given as parameter.
|
||||
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
||||
func Log(level logrus.Level, args ...interface{}) {
|
||||
GetLog().Log(level, args...)
|
||||
}
|
||||
|
||||
func LogFn(level logrus.Level, fn logrus.LogFunction) {
|
||||
GetLog().LogFn(level, fn)
|
||||
}
|
||||
|
||||
func Trace(args ...interface{}) {
|
||||
GetLog().Trace(args...)
|
||||
}
|
||||
|
||||
func Debug(args ...interface{}) {
|
||||
GetLog().Debug(args...)
|
||||
}
|
||||
|
||||
func Info(args ...interface{}) {
|
||||
GetLog().Info(args...)
|
||||
}
|
||||
|
||||
func Print(args ...interface{}) {
|
||||
GetLog().Print(args...)
|
||||
}
|
||||
|
||||
func Warn(args ...interface{}) {
|
||||
GetLog().Warn(args...)
|
||||
}
|
||||
|
||||
func Warning(args ...interface{}) {
|
||||
GetLog().Warning(args...)
|
||||
}
|
||||
|
||||
func Error(args ...interface{}) {
|
||||
GetLog().Error(args...)
|
||||
}
|
||||
|
||||
func Fatal(args ...interface{}) {
|
||||
GetLog().Fatal(args...)
|
||||
}
|
||||
|
||||
func Panic(args ...interface{}) {
|
||||
GetLog().Panic(args...)
|
||||
}
|
||||
|
||||
func TraceFn(fn logrus.LogFunction) {
|
||||
GetLog().TraceFn(fn)
|
||||
}
|
||||
|
||||
func DebugFn(fn logrus.LogFunction) {
|
||||
GetLog().DebugFn(fn)
|
||||
}
|
||||
|
||||
func InfoFn(fn logrus.LogFunction) {
|
||||
GetLog().InfoFn(fn)
|
||||
}
|
||||
|
||||
func PrintFn(fn logrus.LogFunction) {
|
||||
GetLog().PrintFn(fn)
|
||||
}
|
||||
|
||||
func WarnFn(fn logrus.LogFunction) {
|
||||
GetLog().WarnFn(fn)
|
||||
}
|
||||
|
||||
func WarningFn(fn logrus.LogFunction) {
|
||||
GetLog().WarningFn(fn)
|
||||
}
|
||||
|
||||
func ErrorFn(fn logrus.LogFunction) {
|
||||
GetLog().ErrorFn(fn)
|
||||
}
|
||||
|
||||
func FatalFn(fn logrus.LogFunction) {
|
||||
GetLog().FatalFn(fn)
|
||||
}
|
||||
|
||||
func PanicFn(fn logrus.LogFunction) {
|
||||
GetLog().PanicFn(fn)
|
||||
}
|
||||
|
||||
func Logln(level logrus.Level, args ...interface{}) {
|
||||
GetLog().Logln(level, args...)
|
||||
}
|
||||
|
||||
func Traceln(args ...interface{}) {
|
||||
GetLog().Traceln(args...)
|
||||
}
|
||||
|
||||
func Debugln(args ...interface{}) {
|
||||
GetLog().Debugln(args...)
|
||||
}
|
||||
|
||||
func Infoln(args ...interface{}) {
|
||||
GetLog().Infoln(args...)
|
||||
}
|
||||
|
||||
func Println(args ...interface{}) {
|
||||
GetLog().Println(args...)
|
||||
}
|
||||
|
||||
func Warnln(args ...interface{}) {
|
||||
GetLog().Warnln(args...)
|
||||
}
|
||||
|
||||
func Warningln(args ...interface{}) {
|
||||
GetLog().Warningln(args...)
|
||||
}
|
||||
|
||||
func Errorln(args ...interface{}) {
|
||||
GetLog().Errorln(args...)
|
||||
}
|
||||
|
||||
func Fatalln(args ...interface{}) {
|
||||
GetLog().Fatalln(args...)
|
||||
}
|
||||
|
||||
func Panicln(args ...interface{}) {
|
||||
GetLog().Panicln(args...)
|
||||
}
|
||||
|
||||
func Exit(code int) {
|
||||
GetLog().Exit(code)
|
||||
}
|
||||
|
||||
// When file is opened with appending mode, it's safe to
|
||||
// write concurrently to a file (within 4k message on Linux).
|
||||
// In these cases user can choose to disable the lock.
|
||||
func SetNoLock() {
|
||||
GetLog().SetNoLock()
|
||||
}
|
||||
|
||||
//// SetLevel sets the logger level.
|
||||
//func SetLevel(level logrus.Level) {
|
||||
//}
|
||||
|
||||
// GetLevel returns the logger level.
|
||||
func GetLevel() logrus.Level {
|
||||
return GetLog().GetLevel()
|
||||
}
|
||||
|
||||
// AddHook adds a hook to the logger hooks.
|
||||
func AddHook(hook logrus.Hook) {
|
||||
GetLog().AddHook(hook)
|
||||
}
|
||||
|
||||
// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
||||
func IsLevelEnabled(level logrus.Level) bool {
|
||||
return GetLog().IsLevelEnabled(level)
|
||||
}
|
||||
|
||||
// SetFormatter sets the logger formatter.
|
||||
func SetFormatter(formatter logrus.Formatter) {
|
||||
GetLog().SetFormatter(formatter)
|
||||
}
|
||||
|
||||
// SetOutput sets the logger output.
|
||||
func SetOutput(output io.Writer) {
|
||||
GetLog().SetOutput(output)
|
||||
}
|
||||
|
||||
func SetReportCaller(reportCaller bool) {
|
||||
GetLog().SetReportCaller(reportCaller)
|
||||
}
|
||||
|
||||
// ReplaceHooks replaces the logger hooks and returns the old ones
|
||||
func ReplaceHooks(hooks logrus.LevelHooks) logrus.LevelHooks {
|
||||
GetLog()
|
||||
return ReplaceHooks(hooks)
|
||||
}
|
||||
|
||||
// SetBufferPool sets the logger buffer pool.
|
||||
func SetBufferPool(pool logrus.BufferPool) {
|
||||
GetLog().SetBufferPool(pool)
|
||||
}
|
89
vendor/github.com/ManyakRus/starter/logger/logger.go
generated
vendored
Normal file
89
vendor/github.com/ManyakRus/starter/logger/logger.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
// модуль создания единого логирования
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
//"github.com/google/logger"
|
||||
//"github.com/sirupsen/logrus"
|
||||
logrus "github.com/ManyakRus/logrus"
|
||||
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
)
|
||||
|
||||
// log - глобальный логгер приложения
|
||||
var log *logrus.Logger
|
||||
|
||||
// onceLog - гарантирует единственное создание логгера
|
||||
var onceLog sync.Once
|
||||
|
||||
// GetLog - возвращает глобальный логгер приложения
|
||||
// и создаёт логгер если ещё не создан
|
||||
func GetLog() *logrus.Logger {
|
||||
onceLog.Do(func() {
|
||||
|
||||
log = logrus.New()
|
||||
log.SetReportCaller(true)
|
||||
|
||||
Formatter := new(logrus.TextFormatter)
|
||||
Formatter.TimestampFormat = "2006-01-02 15:04:05.000"
|
||||
Formatter.FullTimestamp = true
|
||||
Formatter.CallerPrettyfier = CallerPrettyfier
|
||||
log.SetFormatter(Formatter)
|
||||
|
||||
LOG_LEVEL := os.Getenv("LOG_LEVEL")
|
||||
SetLevel(LOG_LEVEL)
|
||||
|
||||
//LOG_FILE := "log.txt"
|
||||
//file, err := os.OpenFile(LOG_FILE, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0755)
|
||||
//if err != nil {
|
||||
// log.Fatal(err)
|
||||
//}
|
||||
////defer file.Close()
|
||||
//
|
||||
//mw := io.MultiWriter(os.Stderr, file)
|
||||
//logrus.SetOutput(mw)
|
||||
|
||||
//log.SetOutput(os.Stdout)
|
||||
//log.SetOutput(file)
|
||||
|
||||
})
|
||||
|
||||
return log
|
||||
}
|
||||
|
||||
// CallerPrettyfier - форматирует имя файла и номер строки кода
|
||||
func CallerPrettyfier(frame *runtime.Frame) (function string, file string) {
|
||||
fileName := " " + path.Base(frame.File) + ":" + strconv.Itoa(frame.Line) + "\t"
|
||||
FunctionName := frame.Function
|
||||
FunctionName = micro.LastWord(FunctionName)
|
||||
FunctionName = FunctionName + "()" + "\t"
|
||||
return FunctionName, fileName
|
||||
}
|
||||
|
||||
// SetLevel - изменяет уровень логирования
|
||||
func SetLevel(LOG_LEVEL string) {
|
||||
if log == nil {
|
||||
GetLog()
|
||||
}
|
||||
|
||||
if LOG_LEVEL == "" {
|
||||
LOG_LEVEL = "info"
|
||||
}
|
||||
level, err := logrus.ParseLevel(LOG_LEVEL)
|
||||
if err != nil {
|
||||
log.Error("logrus.ParseLevel() error: ", err)
|
||||
}
|
||||
|
||||
if level == log.Level {
|
||||
return
|
||||
}
|
||||
|
||||
log.SetLevel(level)
|
||||
log.Debug("new level: ", LOG_LEVEL)
|
||||
}
|
311
vendor/github.com/ManyakRus/starter/logger/logger_proxy.go
generated
vendored
Normal file
311
vendor/github.com/ManyakRus/starter/logger/logger_proxy.go
generated
vendored
Normal file
@ -0,0 +1,311 @@
|
||||
// дублирует все функции логгера logrus
|
||||
package logger
|
||||
|
||||
//
|
||||
//import (
|
||||
// "context"
|
||||
// "github.com/sirupsen/logrus"
|
||||
// "io"
|
||||
// "time"
|
||||
//)
|
||||
//
|
||||
//// WithField allocates a new entry and adds a field to it.
|
||||
//// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
||||
//// this new returned entry.
|
||||
//// If you want multiple fields, use `WithFields`.
|
||||
//func WithField(key string, value interface{}) *logrus.Entry {
|
||||
// return log.WithField(key, value)
|
||||
//}
|
||||
//
|
||||
//// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
//// each `Field`.
|
||||
//func WithFields(fields logrus.Fields) *logrus.Entry {
|
||||
// GetLog()
|
||||
// return log.WithFields(fields)
|
||||
//}
|
||||
//
|
||||
//// Add an error as single field to the log entry. All it does is call
|
||||
//// `WithError` for the given `error`.
|
||||
//func WithError(err error) *logrus.Entry {
|
||||
// GetLog()
|
||||
// return log.WithError(err)
|
||||
//}
|
||||
//
|
||||
//// Add a context to the log entry.
|
||||
//func WithContext(ctx context.Context) *logrus.Entry {
|
||||
// GetLog()
|
||||
// return log.WithContext(ctx)
|
||||
//}
|
||||
//
|
||||
//// Overrides the time of the log entry.
|
||||
//func WithTime(t time.Time) *logrus.Entry {
|
||||
// GetLog()
|
||||
// return WithTime(t)
|
||||
//}
|
||||
//
|
||||
//func Logf(level logrus.Level, format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Logf(level, format, args...)
|
||||
//}
|
||||
//
|
||||
//func Tracef(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Tracef(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Debugf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Debugf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Infof(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Infof(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Printf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Printf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Warnf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warnf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Warningf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warningf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Errorf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Errorf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Fatalf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Fatalf(format, args...)
|
||||
//}
|
||||
//
|
||||
//func Panicf(format string, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Panicf(format, args...)
|
||||
//}
|
||||
//
|
||||
//// Log will log a message at the level given as parameter.
|
||||
//// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||
//// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
||||
//func Log(level logrus.Level, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Log(level, args...)
|
||||
//}
|
||||
//
|
||||
//func LogFn(level logrus.Level, fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.LogFn(level, fn)
|
||||
//}
|
||||
//
|
||||
//func Trace(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Trace(args...)
|
||||
//}
|
||||
//
|
||||
//func Debug(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Debug(args...)
|
||||
//}
|
||||
//
|
||||
//func Info(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Info(args...)
|
||||
//}
|
||||
//
|
||||
//func Print(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Print(args...)
|
||||
//}
|
||||
//
|
||||
//func Warn(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warn(args...)
|
||||
//}
|
||||
//
|
||||
//func Warning(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warning(args...)
|
||||
//}
|
||||
//
|
||||
//func Error(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Error(args...)
|
||||
//}
|
||||
//
|
||||
//func Fatal(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Fatal(args...)
|
||||
//}
|
||||
//
|
||||
//func Panic(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Panic(args...)
|
||||
//}
|
||||
//
|
||||
//func TraceFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.TraceFn(fn)
|
||||
//}
|
||||
//
|
||||
//func DebugFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.DebugFn(fn)
|
||||
//}
|
||||
//
|
||||
//func InfoFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.InfoFn(fn)
|
||||
//}
|
||||
//
|
||||
//func PrintFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.PrintFn(fn)
|
||||
//}
|
||||
//
|
||||
//func WarnFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.WarnFn(fn)
|
||||
//}
|
||||
//
|
||||
//func WarningFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.WarningFn(fn)
|
||||
//}
|
||||
//
|
||||
//func ErrorFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.ErrorFn(fn)
|
||||
//}
|
||||
//
|
||||
//func FatalFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.FatalFn(fn)
|
||||
//}
|
||||
//
|
||||
//func PanicFn(fn logrus.LogFunction) {
|
||||
// GetLog()
|
||||
// log.PanicFn(fn)
|
||||
//}
|
||||
//
|
||||
//func Logln(level logrus.Level, args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Logln(level, args...)
|
||||
//}
|
||||
//
|
||||
//func Traceln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Traceln(args...)
|
||||
//}
|
||||
//
|
||||
//func Debugln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Debugln(args...)
|
||||
//}
|
||||
//
|
||||
//func Infoln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Infoln(args...)
|
||||
//}
|
||||
//
|
||||
//func Println(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Println(args...)
|
||||
//}
|
||||
//
|
||||
//func Warnln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warnln(args...)
|
||||
//}
|
||||
//
|
||||
//func Warningln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Warningln(args...)
|
||||
//}
|
||||
//
|
||||
//func Errorln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Errorln(args...)
|
||||
//}
|
||||
//
|
||||
//func Fatalln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Fatalln(args...)
|
||||
//}
|
||||
//
|
||||
//func Panicln(args ...interface{}) {
|
||||
// GetLog()
|
||||
// log.Panicln(args...)
|
||||
//}
|
||||
//
|
||||
//func Exit(code int) {
|
||||
// GetLog()
|
||||
// log.Exit(code)
|
||||
//}
|
||||
//
|
||||
////When file is opened with appending mode, it's safe to
|
||||
////write concurrently to a file (within 4k message on Linux).
|
||||
////In these cases user can choose to disable the lock.
|
||||
//func SetNoLock() {
|
||||
// GetLog()
|
||||
// log.SetNoLock()
|
||||
//}
|
||||
//
|
||||
////// SetLevel sets the logger level.
|
||||
////func SetLevel(level logrus.Level) {
|
||||
////}
|
||||
//
|
||||
//// GetLevel returns the logger level.
|
||||
//func GetLevel() logrus.Level {
|
||||
// GetLog()
|
||||
// return log.GetLevel()
|
||||
//}
|
||||
//
|
||||
//// AddHook adds a hook to the logger hooks.
|
||||
//func AddHook(hook logrus.Hook) {
|
||||
// GetLog()
|
||||
// log.AddHook(hook)
|
||||
//}
|
||||
//
|
||||
//// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
||||
//func IsLevelEnabled(level logrus.Level) bool {
|
||||
// GetLog()
|
||||
// return log.IsLevelEnabled(level)
|
||||
//}
|
||||
//
|
||||
//// SetFormatter sets the logger formatter.
|
||||
//func SetFormatter(formatter logrus.Formatter) {
|
||||
// GetLog()
|
||||
// log.SetFormatter(formatter)
|
||||
//}
|
||||
//
|
||||
//// SetOutput sets the logger output.
|
||||
//func SetOutput(output io.Writer) {
|
||||
// GetLog()
|
||||
// log.SetOutput(output)
|
||||
//}
|
||||
//
|
||||
//func SetReportCaller(reportCaller bool) {
|
||||
// GetLog()
|
||||
// log.SetReportCaller(reportCaller)
|
||||
//}
|
||||
//
|
||||
//// ReplaceHooks replaces the logger hooks and returns the old ones
|
||||
//func ReplaceHooks(hooks logrus.LevelHooks) logrus.LevelHooks {
|
||||
// GetLog()
|
||||
// return ReplaceHooks(hooks)
|
||||
//}
|
||||
//
|
||||
//// SetBufferPool sets the logger buffer pool.
|
||||
//func SetBufferPool(pool logrus.BufferPool) {
|
||||
// GetLog()
|
||||
// log.SetBufferPool(pool)
|
||||
//}
|
634
vendor/github.com/ManyakRus/starter/micro/microfunctions.go
generated
vendored
Normal file
634
vendor/github.com/ManyakRus/starter/micro/microfunctions.go
generated
vendored
Normal file
@ -0,0 +1,634 @@
|
||||
// модуль с вспомогательными небольшими функциями
|
||||
|
||||
package micro
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
//"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
//var log = logger.GetLog()
|
||||
|
||||
// IsTestApp - возвращает true если это тестовая среда выполнения приложения
|
||||
func IsTestApp() bool {
|
||||
Otvet := true
|
||||
|
||||
stage, ok := os.LookupEnv("STAGE")
|
||||
if ok == false {
|
||||
panic(fmt.Errorf("Not found Env 'STAGE' !"))
|
||||
}
|
||||
|
||||
switch stage {
|
||||
case "local", "dev", "test", "preprod":
|
||||
Otvet = true
|
||||
case "prod":
|
||||
Otvet = false
|
||||
default:
|
||||
panic(fmt.Errorf("Error, unknown stage(%v) !", stage))
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// FileExists - возвращает true если файл существует
|
||||
func FileExists(name string) (bool, error) {
|
||||
_, err := os.Stat(name)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
// AddSeparator - добавляет в конец строки сеператор "/", если его там нет
|
||||
func AddSeparator(dir string) string {
|
||||
otvet := dir
|
||||
|
||||
if otvet == "" {
|
||||
return SeparatorFile()
|
||||
}
|
||||
|
||||
if otvet[len(otvet)-1:] != SeparatorFile() {
|
||||
otvet = otvet + SeparatorFile()
|
||||
}
|
||||
|
||||
return otvet
|
||||
}
|
||||
|
||||
// SeparatorFile - возвращает символ сепаратора каталогов= / или \
|
||||
func SeparatorFile() string {
|
||||
return string(filepath.Separator)
|
||||
}
|
||||
|
||||
// Sleep - приостановка работы программы на нужное число миллисекунд
|
||||
func Sleep(ms int) {
|
||||
duration := time.Duration(ms) * time.Millisecond
|
||||
time.Sleep(duration)
|
||||
}
|
||||
|
||||
// Pause - приостановка работы программы на нужное число миллисекунд
|
||||
func Pause(ms int) {
|
||||
Sleep(ms)
|
||||
}
|
||||
|
||||
// FindDirUp - возвращает строку с именем каталога на уровень выше
|
||||
func FindDirUp(dir string) string {
|
||||
otvet := dir
|
||||
if dir == "" {
|
||||
return otvet
|
||||
}
|
||||
|
||||
if otvet[len(otvet)-1:] == SeparatorFile() {
|
||||
otvet = otvet[:len(otvet)-1]
|
||||
}
|
||||
|
||||
pos1 := strings.LastIndex(otvet, SeparatorFile())
|
||||
if pos1 > 0 {
|
||||
otvet = otvet[0 : pos1+1]
|
||||
}
|
||||
|
||||
return otvet
|
||||
}
|
||||
|
||||
// ErrorJoin - возвращает ошибку из объединения текста двух ошибок
|
||||
func ErrorJoin(err1, err2 error) error {
|
||||
var err error
|
||||
|
||||
if err1 == nil && err2 == nil {
|
||||
|
||||
} else if err1 == nil {
|
||||
err = err2
|
||||
} else if err2 == nil {
|
||||
err = err1
|
||||
} else {
|
||||
err = errors.New(err1.Error() + ", " + err2.Error())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SubstringLeft - возвращает левые символы строки
|
||||
func SubstringLeft(str string, num int) string {
|
||||
if num <= 0 {
|
||||
return ``
|
||||
}
|
||||
if num > len(str) {
|
||||
num = len(str)
|
||||
}
|
||||
return str[:num]
|
||||
}
|
||||
|
||||
// SubstringRight - возвращает правые символы строки
|
||||
func SubstringRight(str string, num int) string {
|
||||
if num <= 0 {
|
||||
return ``
|
||||
}
|
||||
max := len(str)
|
||||
if num > max {
|
||||
num = max
|
||||
}
|
||||
num = max - num
|
||||
return str[num:]
|
||||
}
|
||||
|
||||
// StringBetween - GetStringInBetween Returns empty string if no start string found
|
||||
func StringBetween(str string, start string, end string) string {
|
||||
otvet := ""
|
||||
if str == "" {
|
||||
return otvet
|
||||
}
|
||||
|
||||
pos1 := strings.Index(str, start)
|
||||
if pos1 == -1 {
|
||||
return otvet
|
||||
}
|
||||
pos1 += len(start)
|
||||
|
||||
pos2 := strings.Index(str[pos1:], end)
|
||||
if pos2 == -1 {
|
||||
return otvet
|
||||
}
|
||||
pos2 = pos1 + pos2
|
||||
|
||||
otvet = str[pos1:pos2]
|
||||
return otvet
|
||||
}
|
||||
|
||||
// LastWord - возвращает последнее слово из строки
|
||||
func LastWord(StringFrom string) string {
|
||||
Otvet := ""
|
||||
|
||||
if StringFrom == "" {
|
||||
return Otvet
|
||||
}
|
||||
|
||||
r := []rune(StringFrom)
|
||||
for f := len(r); f >= 0; f-- {
|
||||
r1 := r[f-1]
|
||||
if r1 == '_' {
|
||||
} else if unicode.IsLetter(r1) == false && unicode.IsDigit(r1) == false {
|
||||
break
|
||||
}
|
||||
|
||||
Otvet = string(r1) + Otvet
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// CurrentFilename - возвращает полное имя текущего исполняемого файла
|
||||
func CurrentFilename() string {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
return filename
|
||||
}
|
||||
|
||||
// ProgramDir - возвращает главный каталог программы, в конце "/"
|
||||
func ProgramDir_Common() string {
|
||||
//filename := os.Args[0]
|
||||
filename, err := os.Executable()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dir := filepath.Dir(filename)
|
||||
sdir := strings.ToLower(dir)
|
||||
|
||||
substr := "/tmp/"
|
||||
pos1 := strings.Index(sdir, substr)
|
||||
if pos1 >= 0 {
|
||||
//linux
|
||||
filename = CurrentFilename()
|
||||
dir = filepath.Dir(filename)
|
||||
|
||||
substr := SeparatorFile() + "vendor" + SeparatorFile()
|
||||
pos_vendor := strings.Index(strings.ToLower(dir), substr)
|
||||
if pos_vendor >= 0 {
|
||||
dir = dir[0:pos_vendor]
|
||||
} else if dir[len(dir)-5:] == "micro" {
|
||||
dir = FindDirUp(dir)
|
||||
//dir = FindDirUp(dir)
|
||||
//dir = FindDirUp(dir)
|
||||
}
|
||||
} else {
|
||||
//Windows
|
||||
substr = "\\temp\\"
|
||||
pos1 = strings.Index(sdir, substr)
|
||||
if pos1 >= 0 {
|
||||
filename = CurrentFilename()
|
||||
dir = filepath.Dir(filename)
|
||||
|
||||
substr := SeparatorFile() + "vendor" + SeparatorFile()
|
||||
pos_vendor := strings.Index(strings.ToLower(dir), substr)
|
||||
if pos_vendor >= 0 {
|
||||
dir = dir[0:pos_vendor]
|
||||
} else if dir[len(dir)-5:] == "micro" {
|
||||
dir = FindDirUp(dir)
|
||||
//dir = FindDirUp(dir)
|
||||
//dir = FindDirUp(dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//dir, err := os.Getwd()
|
||||
//if err != nil {
|
||||
// log.Fatalln(err)
|
||||
// dir = ""
|
||||
//}
|
||||
|
||||
dir = AddSeparator(dir)
|
||||
return dir
|
||||
}
|
||||
|
||||
// ProgramDir - возвращает главный каталог программы, в конце "/"
|
||||
func ProgramDir() string {
|
||||
Otvet := ProgramDir_Common()
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// FileNameWithoutExtension - возвращает имя файла без расширения
|
||||
func FileNameWithoutExtension(fileName string) string {
|
||||
return strings.TrimSuffix(fileName, filepath.Ext(fileName))
|
||||
}
|
||||
|
||||
func BeginningOfMonth(date time.Time) time.Time {
|
||||
return date.AddDate(0, 0, -date.Day()+1)
|
||||
}
|
||||
|
||||
func EndOfMonth(date time.Time) time.Time {
|
||||
Otvet := date.AddDate(0, 1, -date.Day())
|
||||
return Otvet
|
||||
//return date.AddDate(0, 1, -date.Day())
|
||||
}
|
||||
|
||||
//// GetPackageName - возвращает имя пакета
|
||||
//func GetPackageName(temp interface{}) string {
|
||||
// strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".")
|
||||
// strs = strings.Split(strs[len(strs)-2], "/")
|
||||
// return strs[len(strs)-1]
|
||||
//}
|
||||
|
||||
// StringAfter - возвращает строку, начиная после субстроки StringAfter
|
||||
func StringAfter(StringFull, StringAfter string) string {
|
||||
Otvet := StringFull
|
||||
pos1 := strings.Index(StringFull, StringAfter)
|
||||
if pos1 == -1 {
|
||||
return Otvet
|
||||
}
|
||||
|
||||
Otvet = Otvet[pos1+len(StringAfter):]
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// StringFrom - возвращает строку, начиная со субстроки StringAfter
|
||||
func StringFrom(StringFull, StringAfter string) string {
|
||||
Otvet := StringFull
|
||||
pos1 := strings.Index(StringFull, StringAfter)
|
||||
if pos1 == -1 {
|
||||
return Otvet
|
||||
}
|
||||
|
||||
Otvet = Otvet[pos1:]
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
func Trim(s string) string {
|
||||
Otvet := ""
|
||||
|
||||
Otvet = strings.Trim(s, " \n\r\t")
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// Max returns the largest of x or y.
|
||||
func Max(x, y int) int {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Min returns the smallest of x or y.
|
||||
func Min(x, y int) int {
|
||||
if x > y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Max returns the largest of x or y.
|
||||
func MaxInt64(x, y int64) int64 {
|
||||
if x < y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Min returns the smallest of x or y.
|
||||
func MinInt64(x, y int64) int64 {
|
||||
if x > y {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// MaxDate returns the largest of x or y.
|
||||
func MaxDate(x, y time.Time) time.Time {
|
||||
if x.Before(y) == true {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// MinDate returns the smallest of x or y.
|
||||
func MinDate(x, y time.Time) time.Time {
|
||||
if x.Before(y) == false {
|
||||
return y
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// GoGo - запускает функцию в отдельном потоке
|
||||
func GoGo(ctx context.Context, fn func() error) error {
|
||||
var err error
|
||||
chanErr := make(chan error)
|
||||
|
||||
go gogo_chan(fn, chanErr)
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
Text1 := "error: TimeOut"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
case err = <-chanErr:
|
||||
//print("err: ", err)
|
||||
break
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// gogo_chan - запускает функцию и возвращает ошибку в поток
|
||||
// только совместно с GoGo()
|
||||
func gogo_chan(fn func() error, chanErr chan error) {
|
||||
err := fn()
|
||||
chanErr <- err
|
||||
}
|
||||
|
||||
// CheckInnKpp - проверяет правильность ИНН и КПП
|
||||
func CheckInnKpp(Inn, Kpp string, is_individual bool) error {
|
||||
|
||||
var err error
|
||||
|
||||
if Inn == "" {
|
||||
Text1 := "ИНН не должен быть пустой"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
}
|
||||
|
||||
if is_individual == true {
|
||||
if len(Inn) != 12 {
|
||||
Text1 := "Длина ИНН должна быть 12 символов"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
}
|
||||
if len(Kpp) != 0 {
|
||||
Text1 := "КПП должен быть пустой"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if len(Inn) != 10 {
|
||||
Text1 := "Длина ИНН должна быть 10 символов"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
}
|
||||
if len(Kpp) != 9 {
|
||||
Text1 := "КПП должен быть 9 символов"
|
||||
err = errors.New(Text1)
|
||||
return err
|
||||
}
|
||||
|
||||
err = CheckINNControlSum(Inn)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckINNControlSum - проверяет правильность ИНН по контрольной сумме
|
||||
func CheckINNControlSum(Inn string) error {
|
||||
var err error
|
||||
|
||||
if len(Inn) == 10 {
|
||||
err = CheckINNControlSum10(Inn)
|
||||
} else if len(Inn) == 12 {
|
||||
err = CheckINNControlSum12(Inn)
|
||||
} else {
|
||||
err = errors.New("ИНН должен быть 10 или 12 символов")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckINNControlSum10 - проверяет правильность 10-значного ИНН по контрольной сумме
|
||||
func CheckINNControlSum10(Inn string) error {
|
||||
var err error
|
||||
|
||||
MassKoef := [10]int{2, 4, 10, 3, 5, 9, 4, 6, 8, 0}
|
||||
|
||||
var sum int
|
||||
var x int
|
||||
for i, _ := range Inn {
|
||||
s := Inn[i : i+1]
|
||||
var err1 error
|
||||
x, err1 = strconv.Atoi(s)
|
||||
if err1 != nil {
|
||||
err = errors.New("Неправильная цифра в ИНН: " + s)
|
||||
return err
|
||||
}
|
||||
|
||||
sum = sum + x*MassKoef[i]
|
||||
}
|
||||
|
||||
ControlSum := sum % 11
|
||||
ControlSum = ControlSum % 10
|
||||
if ControlSum != x {
|
||||
err = errors.New("Неправильная контрольная сумма ИНН")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CheckINNControlSum2 - проверяет правильность 12-значного ИНН по контрольной сумме
|
||||
func CheckINNControlSum12(Inn string) error {
|
||||
var err error
|
||||
|
||||
//контрольное чилос по 11 знакам
|
||||
MassKoef := [11]int{7, 2, 4, 10, 3, 5, 9, 4, 6, 8, 0}
|
||||
|
||||
var sum int
|
||||
var x11 int
|
||||
for i := 0; i < 11; i++ {
|
||||
s := Inn[i : i+1]
|
||||
var err1 error
|
||||
x, err1 := strconv.Atoi(s)
|
||||
if err1 != nil {
|
||||
err = errors.New("Неправильная цифра в ИНН: " + s)
|
||||
return err
|
||||
}
|
||||
if i == 10 {
|
||||
x11 = x
|
||||
}
|
||||
|
||||
sum = sum + x*MassKoef[i]
|
||||
}
|
||||
|
||||
ControlSum := sum % 11
|
||||
ControlSum = ControlSum % 10
|
||||
|
||||
if ControlSum != x11 {
|
||||
err = errors.New("Неправильная контрольная сумма ИНН")
|
||||
return err
|
||||
}
|
||||
|
||||
//контрольное чилос по 12 знакам
|
||||
MassKoef2 := [12]int{3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8, 0}
|
||||
|
||||
var sum2 int
|
||||
var x12 int
|
||||
for i := 0; i < 12; i++ {
|
||||
s := Inn[i : i+1]
|
||||
var err1 error
|
||||
x, err1 := strconv.Atoi(s)
|
||||
if err1 != nil {
|
||||
err = errors.New("Неправильная цифра в ИНН: " + s)
|
||||
return err
|
||||
}
|
||||
if i == 11 {
|
||||
x12 = x
|
||||
}
|
||||
|
||||
sum2 = sum2 + x*MassKoef2[i]
|
||||
}
|
||||
|
||||
ControlSum2 := sum2 % 11
|
||||
ControlSum2 = ControlSum2 % 10
|
||||
|
||||
if ControlSum2 != x12 {
|
||||
err = errors.New("Неправильная контрольная сумма ИНН")
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// StringFromInt64 - возвращает строку из числа
|
||||
func StringFromInt64(i int64) string {
|
||||
Otvet := ""
|
||||
|
||||
Otvet = strconv.FormatInt(i, 10)
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// StringDate - возвращает строку дата без времени
|
||||
func StringDate(t time.Time) string {
|
||||
Otvet := ""
|
||||
|
||||
Otvet = t.Format("02.01.2006")
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// ProgramDir_bin - возвращает каталог "bin" или каталог программы
|
||||
func ProgramDir_bin() string {
|
||||
Otvet := ""
|
||||
|
||||
dir := ProgramDir()
|
||||
FileName := dir + "bin" + SeparatorFile()
|
||||
|
||||
ok, _ := FileExists(FileName)
|
||||
if ok == true {
|
||||
return FileName
|
||||
}
|
||||
|
||||
Otvet = dir
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// SaveTempFile - записывает массив байт в файл
|
||||
func SaveTempFile(bytes []byte) string {
|
||||
Otvet, err := SaveTempFile_err(bytes)
|
||||
if err != nil {
|
||||
TextError := fmt.Sprint("SaveTempFile() error: ", err)
|
||||
print(TextError)
|
||||
panic(TextError)
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// SaveTempFile_err - записывает массив байт в файл, возвращает ошибку
|
||||
func SaveTempFile_err(bytes []byte) (string, error) {
|
||||
Otvet := ""
|
||||
|
||||
// create and open a temporary file
|
||||
f, err := os.CreateTemp("", "") // in Go version older than 1.17 you can use ioutil.TempFile
|
||||
if err != nil {
|
||||
return Otvet, err
|
||||
}
|
||||
|
||||
// close and remove the temporary file at the end of the program
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
// write data to the temporary file
|
||||
if _, err := f.Write(bytes); err != nil {
|
||||
return Otvet, err
|
||||
}
|
||||
|
||||
Otvet = f.Name()
|
||||
|
||||
return Otvet, err
|
||||
}
|
||||
|
||||
// Hash - возвращает число хэш из строки
|
||||
func Hash(s string) uint32 {
|
||||
h := fnv.New32a()
|
||||
h.Write([]byte(s))
|
||||
return h.Sum32()
|
||||
}
|
||||
|
||||
// TextError - возвращает текст ошибки из error
|
||||
func TextError(err error) string {
|
||||
Otvet := ""
|
||||
|
||||
if err != nil {
|
||||
Otvet = err.Error()
|
||||
}
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// GetType - возвращает строку тип объекта
|
||||
func GetType(myvar interface{}) string {
|
||||
return reflect.TypeOf(myvar).String()
|
||||
}
|
42
vendor/github.com/ManyakRus/starter/ping/ping.go
generated
vendored
Normal file
42
vendor/github.com/ManyakRus/starter/ping/ping.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package ping
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// log - глобальный логгер
|
||||
var log = logger.GetLog()
|
||||
|
||||
func Ping_err(IP, Port string) error {
|
||||
var err error
|
||||
|
||||
var timeout time.Duration
|
||||
timeout = time.Second * 3
|
||||
network := IP + ":" + Port
|
||||
|
||||
conn, err := net.DialTimeout("tcp", network, timeout)
|
||||
|
||||
if err != nil {
|
||||
//log.Warn("PingPort() error: ", err)
|
||||
} else {
|
||||
defer conn.Close()
|
||||
//log.Debug("ping OK: ", network)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func Ping(IP, Port string) {
|
||||
var err error
|
||||
|
||||
network := IP + ":" + Port
|
||||
|
||||
err = Ping_err(IP, Port)
|
||||
if err != nil {
|
||||
log.Panic("Ping() error: ", err)
|
||||
} else {
|
||||
log.Debug("Ping() OK: ", network)
|
||||
}
|
||||
}
|
291
vendor/github.com/ManyakRus/starter/postgres_connect/postgres_connect.go
generated
vendored
Normal file
291
vendor/github.com/ManyakRus/starter/postgres_connect/postgres_connect.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
// модуль для работы с базой данных
|
||||
|
||||
package postgres_connect
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"github.com/ManyakRus/starter/ping"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ManyakRus/starter/contextmain"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"github.com/ManyakRus/starter/stopapp"
|
||||
)
|
||||
|
||||
// Conn - соединение к базе данных
|
||||
var Conn *sqlx.DB
|
||||
|
||||
// log - глобальный логгер
|
||||
var log = logger.GetLog()
|
||||
|
||||
// mutexReconnect - защита от многопоточности Reconnect()
|
||||
var mutexReconnect = &sync.Mutex{}
|
||||
|
||||
// Settings хранит все нужные переменные окружения
|
||||
var Settings SettingsINI
|
||||
|
||||
// NeedReconnect - флаг необходимости переподключения
|
||||
var NeedReconnect bool
|
||||
|
||||
// SettingsINI - структура для хранения всех нужных переменных окружения
|
||||
type SettingsINI struct {
|
||||
DB_HOST string
|
||||
DB_PORT string
|
||||
DB_NAME string
|
||||
DB_SCHEMA string
|
||||
DB_USER string
|
||||
DB_PASSWORD string
|
||||
}
|
||||
|
||||
// Connect_err - подключается к базе данных
|
||||
func Connect() {
|
||||
|
||||
if Settings.DB_HOST == "" {
|
||||
FillSettings()
|
||||
}
|
||||
|
||||
ping.Ping(Settings.DB_HOST, Settings.DB_PORT)
|
||||
|
||||
err := Connect_err()
|
||||
if err != nil {
|
||||
log.Panicln("POSTGRES sqlx Connect_err() host: ", Settings.DB_HOST, ", Error: ", err)
|
||||
} else {
|
||||
log.Info("POSTGRES sqlx connected. host: ", Settings.DB_HOST, ", base name: ", Settings.DB_NAME, ", schema: ", Settings.DB_SCHEMA)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Connect_err - подключается к базе данных
|
||||
func Connect_err() error {
|
||||
var err error
|
||||
|
||||
if Settings.DB_HOST == "" {
|
||||
FillSettings()
|
||||
}
|
||||
|
||||
//ctxMain := context.Background()
|
||||
//ctxMain := contextmain.GetContext()
|
||||
//ctx, cancel := context.WithTimeout(ctxMain, 5*time.Second)
|
||||
//defer cancel()
|
||||
|
||||
// get the database connection URL.
|
||||
databaseUrl := "postgres://" + Settings.DB_USER + ":" + Settings.DB_PASSWORD
|
||||
databaseUrl += "@" + Settings.DB_HOST + ":5432/" + Settings.DB_NAME + "?sslmode=disable"
|
||||
|
||||
//
|
||||
//Conn, err = pgx.Connect(ctx, databaseUrl)
|
||||
Conn, err = sqlx.Connect(
|
||||
"postgres",
|
||||
databaseUrl,
|
||||
)
|
||||
if err == nil {
|
||||
err = Conn.Ping()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// IsClosed проверка что база данных закрыта
|
||||
func IsClosed() bool {
|
||||
var otvet bool
|
||||
if Conn == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
ctx := contextmain.GetContext()
|
||||
err := Conn.PingContext(ctx)
|
||||
if err != nil {
|
||||
otvet = true
|
||||
}
|
||||
return otvet
|
||||
}
|
||||
|
||||
// Reconnect повторное подключение к базе данных, если оно отключено
|
||||
// или полная остановка программы
|
||||
func Reconnect(err error) {
|
||||
mutexReconnect.Lock()
|
||||
defer mutexReconnect.Unlock()
|
||||
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
|
||||
if Conn == nil {
|
||||
log.Warn("Reconnect()")
|
||||
err := Connect_err()
|
||||
if err != nil {
|
||||
log.Error("error: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if IsClosed() {
|
||||
micro.Pause(1000)
|
||||
log.Warn("Reconnect()")
|
||||
err := Connect_err()
|
||||
if err != nil {
|
||||
log.Error("error: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
sError := err.Error()
|
||||
if sError == "Conn closed" {
|
||||
micro.Pause(1000)
|
||||
log.Warn("Reconnect()")
|
||||
err := Connect_err()
|
||||
if err != nil {
|
||||
log.Error("error: ", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//PgError, ok := err.(*pgconn.PgError)
|
||||
//if ok {
|
||||
// if PgError.Code == "P0001" { // Class P0 — PL/pgSQL Error, RaiseException
|
||||
// return //нужен
|
||||
// }
|
||||
//}
|
||||
|
||||
//остановим программу т.к. она не должна работать при неработающеё БД
|
||||
log.Error("STOP app. Error: ", err)
|
||||
stopapp.StopApp()
|
||||
|
||||
}
|
||||
|
||||
// CloseConnection - закрытие соединения с базой данных
|
||||
func CloseConnection() error {
|
||||
if Conn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := CloseConnection_err()
|
||||
if err != nil {
|
||||
log.Error("Postgres sqlx CloseConnection() error: ", err)
|
||||
} else {
|
||||
log.Info("Postgres sqlx connection closed")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseConnection - закрытие соединения с базой данных
|
||||
func CloseConnection_err() error {
|
||||
if Conn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//ctx := contextmain.GetContext()
|
||||
//ctx := context.Background()
|
||||
err := Conn.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// WaitStop - ожидает отмену глобального контекста
|
||||
func WaitStop() {
|
||||
|
||||
select {
|
||||
case <-contextmain.GetContext().Done():
|
||||
log.Warn("Context app is canceled.")
|
||||
}
|
||||
|
||||
//
|
||||
stopapp.WaitTotalMessagesSendingNow("Postgres sqlx")
|
||||
|
||||
//
|
||||
err := CloseConnection()
|
||||
if err != nil {
|
||||
log.Error("CloseConnection() error: ", err)
|
||||
}
|
||||
stopapp.GetWaitGroup_Main().Done()
|
||||
}
|
||||
|
||||
// StartDB - делает соединение с БД, отключение и др.
|
||||
func StartDB() {
|
||||
Connect()
|
||||
|
||||
stopapp.GetWaitGroup_Main().Add(1)
|
||||
go WaitStop()
|
||||
|
||||
stopapp.GetWaitGroup_Main().Add(1)
|
||||
go ping_go()
|
||||
|
||||
}
|
||||
|
||||
// FillSettings загружает переменные окружения в структуру из переменных окружения
|
||||
func FillSettings() {
|
||||
Settings = SettingsINI{}
|
||||
Settings.DB_HOST = os.Getenv("DB_HOST")
|
||||
Settings.DB_PORT = os.Getenv("DB_PORT")
|
||||
Settings.DB_NAME = os.Getenv("DB_NAME")
|
||||
Settings.DB_SCHEMA = os.Getenv("DB_SCHEME")
|
||||
Settings.DB_USER = os.Getenv("DB_USER")
|
||||
Settings.DB_PASSWORD = os.Getenv("DB_PASSWORD")
|
||||
|
||||
if Settings.DB_HOST == "" {
|
||||
log.Panicln("Need fill DB_HOST ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.DB_PORT == "" {
|
||||
log.Panicln("Need fill DB_PORT ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.DB_NAME == "" {
|
||||
log.Panicln("Need fill DB_NAME ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.DB_SCHEMA == "" {
|
||||
log.Panicln("Need fill DB_SCHEME ! in os.ENV ")
|
||||
}
|
||||
if Settings.DB_USER == "" {
|
||||
log.Panicln("Need fill DB_USER ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.DB_PASSWORD == "" {
|
||||
log.Panicln("Need fill DB_PASSWORD ! in os.ENV ")
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
// ping_go - делает пинг каждые 60 секунд, и реконнект
|
||||
func ping_go() {
|
||||
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
|
||||
addr := Settings.DB_HOST + ":" + Settings.DB_PORT
|
||||
|
||||
//бесконечный цикл
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-contextmain.GetContext().Done():
|
||||
log.Warn("Context app is canceled. postgres_connect.ping")
|
||||
break loop
|
||||
case <-ticker.C:
|
||||
err := ping.Ping_err(Settings.DB_HOST, Settings.DB_PORT)
|
||||
//log.Debug("ticker, ping err: ", err) //удалить
|
||||
if err != nil {
|
||||
NeedReconnect = true
|
||||
log.Warn("postgres_connect Ping(", addr, ") error: ", err)
|
||||
} else if NeedReconnect == true {
|
||||
log.Warn("postgres_connect Ping(", addr, ") OK. Start Reconnect()")
|
||||
NeedReconnect = false
|
||||
Connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopapp.GetWaitGroup_Main().Done()
|
||||
}
|
127
vendor/github.com/ManyakRus/starter/stopapp/stopapp.go
generated
vendored
Normal file
127
vendor/github.com/ManyakRus/starter/stopapp/stopapp.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// модуль для корректной остановки работы приложения
|
||||
|
||||
package stopapp
|
||||
|
||||
import (
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
//"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/ManyakRus/starter/contextmain"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
// "gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/micro"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/db"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/grpcserver"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/logger"
|
||||
//"gitlab.aescorp.ru/dsp_dev/notifier/notifier_adp_eml/internal/v0/app/micro"
|
||||
)
|
||||
|
||||
// log - глобальный логгер
|
||||
var log = logger.GetLog()
|
||||
|
||||
// SignalInterrupt - канал для ожидания сигнала остановки приложения
|
||||
var SignalInterrupt chan os.Signal
|
||||
|
||||
// wgMain - группа ожидания завершения всех частей программы
|
||||
var wgMain *sync.WaitGroup
|
||||
|
||||
// lockWGMain - гарантирует получение WGMain с учётом многопоточности
|
||||
var lockWGMain = &sync.Mutex{}
|
||||
|
||||
// onceWGMain - гарантирует создание WGMain один раз
|
||||
var onceWGMain sync.Once
|
||||
|
||||
// TotalMessagesSendingNow - количество сообщений отправляющихся прям сейчас
|
||||
var TotalMessagesSendingNow int32
|
||||
|
||||
// SecondsWaitTotalMessagesSendingNow - количество секунд ожидания для отправки последнего сообщения
|
||||
const SecondsWaitTotalMessagesSendingNow = 10
|
||||
|
||||
// SetWaitGroup_Main - присваивает внешний WaitGroup
|
||||
func SetWaitGroup_Main(wg *sync.WaitGroup) {
|
||||
wgMain = wg
|
||||
}
|
||||
|
||||
// GetWaitGroup_Main - возвращает группу ожидания завершения всех частей программы
|
||||
func GetWaitGroup_Main() *sync.WaitGroup {
|
||||
lockWGMain.Lock()
|
||||
defer lockWGMain.Unlock()
|
||||
//
|
||||
//if wgMain == nil {
|
||||
// wgMain = &sync.WaitGroup{}
|
||||
//}
|
||||
|
||||
if wgMain == nil {
|
||||
//onceWGMain.Do(func() {
|
||||
wgMain = &sync.WaitGroup{}
|
||||
//})
|
||||
}
|
||||
|
||||
return wgMain
|
||||
}
|
||||
|
||||
// StartWaitStop - запускает ожидание сигнала завершения приложения
|
||||
func StartWaitStop() {
|
||||
SignalInterrupt = make(chan os.Signal, 1)
|
||||
|
||||
fnWait := func() {
|
||||
signal.Notify(SignalInterrupt, os.Interrupt, syscall.SIGTERM)
|
||||
}
|
||||
go fnWait()
|
||||
|
||||
GetWaitGroup_Main().Add(1)
|
||||
go WaitStop()
|
||||
|
||||
}
|
||||
|
||||
// StopApp - отмена глобального контекста для остановки работы приложения
|
||||
func StopApp() {
|
||||
if contextmain.CancelContext != nil {
|
||||
contextmain.CancelContext()
|
||||
} else {
|
||||
//os.Exit(0)
|
||||
log.Warn("Context = nil")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// StopApp - отмена глобального контекста для остановки работы приложения
|
||||
func StopAppAndWait() {
|
||||
if contextmain.CancelContext != nil {
|
||||
contextmain.CancelContext()
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
GetWaitGroup_Main().Wait()
|
||||
}
|
||||
|
||||
// WaitStop - ожидает отмену глобального контекста или сигнала завершения приложения
|
||||
func WaitStop() {
|
||||
|
||||
select {
|
||||
case <-SignalInterrupt:
|
||||
log.Warn("Interrupt clean shutdown.")
|
||||
contextmain.CancelContext()
|
||||
case <-contextmain.GetContext().Done():
|
||||
log.Warn("Context app is canceled.")
|
||||
}
|
||||
|
||||
GetWaitGroup_Main().Done()
|
||||
}
|
||||
|
||||
// ожидает чтоб прям щас ничего не отправлялось
|
||||
func WaitTotalMessagesSendingNow(filename string) {
|
||||
for f := 0; f < SecondsWaitTotalMessagesSendingNow; f++ {
|
||||
TotalMessages := atomic.LoadInt32(&TotalMessagesSendingNow)
|
||||
if TotalMessages == 0 {
|
||||
break
|
||||
}
|
||||
log.Warn("TotalMessagesSendingNow =", TotalMessages, " !=0 sleep(1000), filename: ", filename)
|
||||
micro.Sleep(1000)
|
||||
}
|
||||
}
|
329
vendor/github.com/ManyakRus/starter/whatsapp_connect/whatsapp_connect.go
generated
vendored
Normal file
329
vendor/github.com/ManyakRus/starter/whatsapp_connect/whatsapp_connect.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
package whatsapp_connect
|
||||
|
||||
//Licenses:
|
||||
//Whatsapp клиент - go.mau.fi/whatsmeow" - MPL-2.0
|
||||
//QR-код - github.com/mdp/qrterminal/v3" - MIT License
|
||||
//SQL сервер - "github.com/mattn/go-sqlite3" - MIT License
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ManyakRus/starter/logger"
|
||||
"go.mau.fi/whatsmeow/types/events"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/mdp/qrterminal/v3"
|
||||
"go.mau.fi/whatsmeow"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/store/sqlstore"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
|
||||
"github.com/ManyakRus/starter/contextmain"
|
||||
"github.com/ManyakRus/starter/micro"
|
||||
"github.com/ManyakRus/starter/stopapp"
|
||||
)
|
||||
|
||||
// clientWhatsApp - клиент соединения мессенджера Whatsapp
|
||||
var clientWhatsApp *whatsmeow.Client
|
||||
|
||||
// log - глобальный логгер приложения
|
||||
var log = logger.GetLog()
|
||||
|
||||
// filenameDB - имя файла локально базы данных sqllite
|
||||
var filenameDB string
|
||||
|
||||
// MaxSendMessageCountIn1Second - максимальное количество сообщений в 1 секунду
|
||||
var MaxSendMessageCountIn1Second float32 = 0.1
|
||||
|
||||
// lastSendTime - время последней отправки сообщения и мьютекс
|
||||
var lastSendTime = lastSendTimeMutex{}
|
||||
|
||||
// lastSendTimeMutex - структура хранения времени последней отправки и мьютекс
|
||||
type lastSendTimeMutex struct {
|
||||
time time.Time
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// Settings хранит все нужные переменные окружения
|
||||
var Settings SettingsINI
|
||||
|
||||
// SettingsINI - структура для хранения всех нужных переменных окружения
|
||||
type SettingsINI struct {
|
||||
WHATSAPP_PHONE_FROM string
|
||||
WHATSAPP_PHONE_SEND_TEST string
|
||||
}
|
||||
|
||||
// MessageWhatsapp - сообщение из WhatsApp сокращённо
|
||||
type MessageWhatsapp struct {
|
||||
Text string
|
||||
NameFrom string
|
||||
PhoneFrom string
|
||||
PhoneChat string
|
||||
IsFromMe bool
|
||||
MediaType string
|
||||
//NameTo string
|
||||
IsGroup bool
|
||||
ID string
|
||||
TimeSent time.Time
|
||||
}
|
||||
|
||||
func FillMessageWhatsapp(mess *events.Message) MessageWhatsapp {
|
||||
Otvet := MessageWhatsapp{}
|
||||
|
||||
Otvet.ID = mess.Info.ID
|
||||
Otvet.PhoneFrom = mess.Info.Sender.User
|
||||
Otvet.IsFromMe = mess.Info.IsFromMe
|
||||
Otvet.MediaType = mess.Info.MediaType
|
||||
Otvet.NameFrom = mess.Info.PushName
|
||||
Otvet.IsGroup = mess.Info.IsGroup
|
||||
if mess.Message != nil && mess.Message.Conversation != nil {
|
||||
//простое сообщение
|
||||
Otvet.Text = *mess.Message.Conversation
|
||||
} else if mess.Message != nil && mess.Message.ExtendedTextMessage != nil {
|
||||
//сообщение ответ
|
||||
Otvet.Text = *mess.Message.ExtendedTextMessage.Text
|
||||
}
|
||||
|
||||
Otvet.PhoneChat = mess.Info.Chat.User
|
||||
Otvet.TimeSent = mess.Info.Timestamp
|
||||
|
||||
return Otvet
|
||||
}
|
||||
|
||||
// SendMessage - отправка сообщения в мессенджер Телеграм
|
||||
// возвращает:
|
||||
// id = id отправленного сообщения в WhatsApp
|
||||
// err = error
|
||||
func SendMessage(phone_send_to string, text string) (string, error) {
|
||||
var id string
|
||||
//var is_sent bool
|
||||
|
||||
TimeLimit()
|
||||
log.Debug("phone_send_to: ", phone_send_to, " text: "+text)
|
||||
//
|
||||
|
||||
ctxMain := context.Background()
|
||||
ctx, cancel := context.WithTimeout(ctxMain, 120*time.Second)
|
||||
defer cancel()
|
||||
|
||||
recipient, ok := ParseJID(phone_send_to)
|
||||
if !ok {
|
||||
text1 := "ParseJID() invalid JID: " + phone_send_to
|
||||
log.Error(text1)
|
||||
return id, errors.New(text1)
|
||||
}
|
||||
|
||||
MessageID := whatsmeow.GenerateMessageID()
|
||||
message1 := &waProto.Message{}
|
||||
message1.Conversation = &text
|
||||
_, err := clientWhatsApp.SendMessage(ctx, recipient, message1)
|
||||
if err != nil {
|
||||
text1 := "Message not sent, to: " + phone_send_to + " !"
|
||||
log.Error(text1)
|
||||
err = errors.New(text1)
|
||||
return "", err
|
||||
}
|
||||
|
||||
id = string(MessageID)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// eventHandler - получение событий из сервера whatsapp
|
||||
func eventHandler_test(evt interface{}) {
|
||||
if evt == nil {
|
||||
log.Error("evt is null !")
|
||||
}
|
||||
switch v := evt.(type) {
|
||||
case *events.Message:
|
||||
mess := evt.(*events.Message)
|
||||
messW := FillMessageWhatsapp(mess)
|
||||
fmt.Println("Received a message from: ", messW.NameFrom, " phone: ", messW.PhoneFrom, "text: ", messW.Text)
|
||||
//fmt.Println("Received a message: ", mess.Message, " from: ", v.Message.GetContactMessage(), "text: ", v.Info.MediaType)
|
||||
default:
|
||||
fmt.Printf("Received: %#v \n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Connect - создание клиента Whatsapp
|
||||
func Connect(eventHandler func(evt interface{})) {
|
||||
err := Connect_err(eventHandler)
|
||||
if err != nil {
|
||||
log.Panic("WHATSAPP Connect_err() error: ", err)
|
||||
} else {
|
||||
log.Info("WHATSAPP connected. Phone from: ", Settings.WHATSAPP_PHONE_FROM)
|
||||
}
|
||||
}
|
||||
|
||||
// Connect_err - создание клиента Whatsapp, и возвращает ошибку
|
||||
func Connect_err(eventHandler func(evt interface{})) error {
|
||||
|
||||
if Settings.WHATSAPP_PHONE_FROM == "" {
|
||||
FillSettings()
|
||||
}
|
||||
|
||||
//ProgramDir := programdir.ProgramDir()
|
||||
ProgramDir := micro.ProgramDir_Common()
|
||||
filenameDB = ProgramDir + "whatsapp.db"
|
||||
|
||||
dbLog := waLog.Stdout("Database", "WARN", true)
|
||||
container, err := sqlstore.New("sqlite3", "file:"+filenameDB+"?_foreign_keys=on", dbLog)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// If you want multiple sessions, remember their JIDs and use .GetDevice(jid) or .GetAllDevices() instead.
|
||||
deviceStore, err := container.GetFirstDevice()
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
clientLog := waLog.Stdout("Client", "WARN", true)
|
||||
clientWhatsApp = whatsmeow.NewClient(deviceStore, clientLog)
|
||||
clientWhatsApp.AddEventHandler(eventHandler)
|
||||
|
||||
if clientWhatsApp.Store.ID == nil {
|
||||
// No ID stored, new login
|
||||
qrChan, _ := clientWhatsApp.GetQRChannel(context.Background())
|
||||
err = clientWhatsApp.Connect()
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
for evt := range qrChan {
|
||||
if evt.Event == "code" {
|
||||
// Render the QR code here
|
||||
qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
|
||||
// or just manually `echo 2@... | qrencode -t ansiutf8` in a terminal
|
||||
log.Println("QR code:", evt.Code)
|
||||
} else {
|
||||
log.Println("Login event:", evt.Event)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Already logged in, just connect
|
||||
err = clientWhatsApp.Connect()
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
|
||||
stopapp.GetWaitGroup_Main().Add(1)
|
||||
go WaitStop()
|
||||
|
||||
//StopWhatsApp()
|
||||
return err
|
||||
}
|
||||
|
||||
// StopWhatsApp - остановка работы клиента мессенджера Whatsapp
|
||||
func StopWhatsApp() {
|
||||
clientWhatsApp.Disconnect()
|
||||
|
||||
}
|
||||
|
||||
// WaitStop - ожидает отмену глобального контекста
|
||||
func WaitStop() {
|
||||
|
||||
select {
|
||||
case <-contextmain.GetContext().Done():
|
||||
log.Warn("Context app is canceled.")
|
||||
}
|
||||
|
||||
//
|
||||
stopapp.WaitTotalMessagesSendingNow("whatsapp")
|
||||
|
||||
//
|
||||
StopWhatsApp()
|
||||
stopapp.GetWaitGroup_Main().Done()
|
||||
}
|
||||
|
||||
// ParseJID parses a JID out of the given string. It supports both regular and AD JIDs.
|
||||
func ParseJID(arg string) (types.JID, bool) {
|
||||
if arg == "" {
|
||||
return types.NewJID(arg, types.DefaultUserServer), false
|
||||
}
|
||||
|
||||
if arg[0] == '+' {
|
||||
arg = arg[1:]
|
||||
}
|
||||
if !strings.ContainsRune(arg, '@') {
|
||||
return types.NewJID(arg, types.DefaultUserServer), true
|
||||
} else {
|
||||
recipient, err := types.ParseJID(arg)
|
||||
if err != nil {
|
||||
_ = fmt.Errorf("Invalid JID %s: %v", arg, err)
|
||||
return recipient, false
|
||||
} else if recipient.User == "" {
|
||||
_ = fmt.Errorf("Invalid JID %s: no server specified", arg)
|
||||
return recipient, false
|
||||
}
|
||||
return recipient, true
|
||||
}
|
||||
}
|
||||
|
||||
// TimeLimit - пауза для ограничения количество сообщений в секунду
|
||||
func TimeLimit() {
|
||||
//if MaxSendMessageCountIn1Second == 0 {
|
||||
// return
|
||||
//}
|
||||
|
||||
lastSendTime.Lock()
|
||||
defer lastSendTime.Unlock()
|
||||
|
||||
if lastSendTime.time.IsZero() {
|
||||
lastSendTime.time = time.Now()
|
||||
return
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
ms := int(t.Sub(lastSendTime.time).Milliseconds())
|
||||
msNeedWait := int(1000 / MaxSendMessageCountIn1Second)
|
||||
if ms < msNeedWait {
|
||||
micro.Sleep(msNeedWait - ms)
|
||||
}
|
||||
|
||||
lastSendTime.time = time.Now()
|
||||
}
|
||||
|
||||
// FillSettings загружает переменные окружения в структуру из файла или из переменных окружения
|
||||
func FillSettings() {
|
||||
Settings = SettingsINI{}
|
||||
Settings.WHATSAPP_PHONE_FROM = os.Getenv("WHATSAPP_PHONE_FROM")
|
||||
Settings.WHATSAPP_PHONE_SEND_TEST = os.Getenv("WHATSAPP_PHONE_SEND_TEST")
|
||||
|
||||
if Settings.WHATSAPP_PHONE_FROM == "" {
|
||||
log.Panicln("Need fill WHATSAPP_PHONE_FROM ! in os.ENV ")
|
||||
}
|
||||
|
||||
if Settings.WHATSAPP_PHONE_SEND_TEST == "" {
|
||||
log.Panicln("Need fill WHATSAPP_PHONE_SEND_TEST ! in os.ENV ")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Start - делает соединение с БД, отключение и др.
|
||||
func Start(eventHandler func(evt interface{})) {
|
||||
Connect(eventHandler)
|
||||
|
||||
stopapp.GetWaitGroup_Main().Add(1)
|
||||
go WaitStop()
|
||||
|
||||
}
|
||||
|
||||
func (m MessageWhatsapp) String() string {
|
||||
Otvet := ""
|
||||
|
||||
Otvet = Otvet + fmt.Sprint("Text: ", m.Text, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("NameFrom: ", m.NameFrom, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("PhoneFrom: ", m.PhoneFrom, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("PhoneChat: ", m.PhoneChat, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("IsFromMe: ", m.IsFromMe, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("MediaType: ", m.MediaType, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("IsGroup: ", m.IsGroup, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("ID: ", m.ID, "\n")
|
||||
Otvet = Otvet + fmt.Sprint("TimeSent: ", m.TimeSent, "\n")
|
||||
|
||||
return Otvet
|
||||
}
|
12
vendor/github.com/beevik/etree/CONTRIBUTORS
generated
vendored
Normal file
12
vendor/github.com/beevik/etree/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
Brett Vickers (beevik)
|
||||
Felix Geisendörfer (felixge)
|
||||
Kamil Kisiel (kisielk)
|
||||
Graham King (grahamking)
|
||||
Matt Smith (ma314smith)
|
||||
Michal Jemala (michaljemala)
|
||||
Nicolas Piganeau (npiganeau)
|
||||
Chris Brown (ccbrown)
|
||||
Earncef Sequeira (earncef)
|
||||
Gabriel de Labachelerie (wuzuf)
|
||||
Martin Dosch (mdosch)
|
||||
Hugo Wetterberg (hugowetterberg)
|
24
vendor/github.com/beevik/etree/LICENSE
generated
vendored
Normal file
24
vendor/github.com/beevik/etree/LICENSE
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
Copyright 2015-2023 Brett Vickers. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
204
vendor/github.com/beevik/etree/README.md
generated
vendored
Normal file
204
vendor/github.com/beevik/etree/README.md
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
[![GoDoc](https://godoc.org/github.com/beevik/etree?status.svg)](https://godoc.org/github.com/beevik/etree)
|
||||
|
||||
etree
|
||||
=====
|
||||
|
||||
The etree package is a lightweight, pure go package that expresses XML in
|
||||
the form of an element tree. Its design was inspired by the Python
|
||||
[ElementTree](http://docs.python.org/2/library/xml.etree.elementtree.html)
|
||||
module.
|
||||
|
||||
Some of the package's capabilities and features:
|
||||
|
||||
* Represents XML documents as trees of elements for easy traversal.
|
||||
* Imports, serializes, modifies or creates XML documents from scratch.
|
||||
* Writes and reads XML to/from files, byte slices, strings and io interfaces.
|
||||
* Performs simple or complex searches with lightweight XPath-like query APIs.
|
||||
* Auto-indents XML using spaces or tabs for better readability.
|
||||
* Implemented in pure go; depends only on standard go libraries.
|
||||
* Built on top of the go [encoding/xml](http://golang.org/pkg/encoding/xml)
|
||||
package.
|
||||
|
||||
### Creating an XML document
|
||||
|
||||
The following example creates an XML document from scratch using the etree
|
||||
package and outputs its indented contents to stdout.
|
||||
```go
|
||||
doc := etree.NewDocument()
|
||||
doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`)
|
||||
doc.CreateProcInst("xml-stylesheet", `type="text/xsl" href="style.xsl"`)
|
||||
|
||||
people := doc.CreateElement("People")
|
||||
people.CreateComment("These are all known people")
|
||||
|
||||
jon := people.CreateElement("Person")
|
||||
jon.CreateAttr("name", "Jon")
|
||||
|
||||
sally := people.CreateElement("Person")
|
||||
sally.CreateAttr("name", "Sally")
|
||||
|
||||
doc.Indent(2)
|
||||
doc.WriteTo(os.Stdout)
|
||||
```
|
||||
|
||||
Output:
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="style.xsl"?>
|
||||
<People>
|
||||
<!--These are all known people-->
|
||||
<Person name="Jon"/>
|
||||
<Person name="Sally"/>
|
||||
</People>
|
||||
```
|
||||
|
||||
### Reading an XML file
|
||||
|
||||
Suppose you have a file on disk called `bookstore.xml` containing the
|
||||
following data:
|
||||
|
||||
```xml
|
||||
<bookstore xmlns:p="urn:schemas-books-com:prices">
|
||||
|
||||
<book category="COOKING">
|
||||
<title lang="en">Everyday Italian</title>
|
||||
<author>Giada De Laurentiis</author>
|
||||
<year>2005</year>
|
||||
<p:price>30.00</p:price>
|
||||
</book>
|
||||
|
||||
<book category="CHILDREN">
|
||||
<title lang="en">Harry Potter</title>
|
||||
<author>J K. Rowling</author>
|
||||
<year>2005</year>
|
||||
<p:price>29.99</p:price>
|
||||
</book>
|
||||
|
||||
<book category="WEB">
|
||||
<title lang="en">XQuery Kick Start</title>
|
||||
<author>James McGovern</author>
|
||||
<author>Per Bothner</author>
|
||||
<author>Kurt Cagle</author>
|
||||
<author>James Linn</author>
|
||||
<author>Vaidyanathan Nagarajan</author>
|
||||
<year>2003</year>
|
||||
<p:price>49.99</p:price>
|
||||
</book>
|
||||
|
||||
<book category="WEB">
|
||||
<title lang="en">Learning XML</title>
|
||||
<author>Erik T. Ray</author>
|
||||
<year>2003</year>
|
||||
<p:price>39.95</p:price>
|
||||
</book>
|
||||
|
||||
</bookstore>
|
||||
```
|
||||
|
||||
This code reads the file's contents into an etree document.
|
||||
```go
|
||||
doc := etree.NewDocument()
|
||||
if err := doc.ReadFromFile("bookstore.xml"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
You can also read XML from a string, a byte slice, or an `io.Reader`.
|
||||
|
||||
### Processing elements and attributes
|
||||
|
||||
This example illustrates several ways to access elements and attributes using
|
||||
etree selection queries.
|
||||
```go
|
||||
root := doc.SelectElement("bookstore")
|
||||
fmt.Println("ROOT element:", root.Tag)
|
||||
|
||||
for _, book := range root.SelectElements("book") {
|
||||
fmt.Println("CHILD element:", book.Tag)
|
||||
if title := book.SelectElement("title"); title != nil {
|
||||
lang := title.SelectAttrValue("lang", "unknown")
|
||||
fmt.Printf(" TITLE: %s (%s)\n", title.Text(), lang)
|
||||
}
|
||||
for _, attr := range book.Attr {
|
||||
fmt.Printf(" ATTR: %s=%s\n", attr.Key, attr.Value)
|
||||
}
|
||||
}
|
||||
```
|
||||
Output:
|
||||
```
|
||||
ROOT element: bookstore
|
||||
CHILD element: book
|
||||
TITLE: Everyday Italian (en)
|
||||
ATTR: category=COOKING
|
||||
CHILD element: book
|
||||
TITLE: Harry Potter (en)
|
||||
ATTR: category=CHILDREN
|
||||
CHILD element: book
|
||||
TITLE: XQuery Kick Start (en)
|
||||
ATTR: category=WEB
|
||||
CHILD element: book
|
||||
TITLE: Learning XML (en)
|
||||
ATTR: category=WEB
|
||||
```
|
||||
|
||||
### Path queries
|
||||
|
||||
This example uses etree's path functions to select all book titles that fall
|
||||
into the category of 'WEB'. The double-slash prefix in the path causes the
|
||||
search for book elements to occur recursively; book elements may appear at any
|
||||
level of the XML hierarchy.
|
||||
```go
|
||||
for _, t := range doc.FindElements("//book[@category='WEB']/title") {
|
||||
fmt.Println("Title:", t.Text())
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Title: XQuery Kick Start
|
||||
Title: Learning XML
|
||||
```
|
||||
|
||||
This example finds the first book element under the root bookstore element and
|
||||
outputs the tag and text of each of its child elements.
|
||||
```go
|
||||
for _, e := range doc.FindElements("./bookstore/book[1]/*") {
|
||||
fmt.Printf("%s: %s\n", e.Tag, e.Text())
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
title: Everyday Italian
|
||||
author: Giada De Laurentiis
|
||||
year: 2005
|
||||
price: 30.00
|
||||
```
|
||||
|
||||
This example finds all books with a price of 49.99 and outputs their titles.
|
||||
```go
|
||||
path := etree.MustCompilePath("./bookstore/book[p:price='49.99']/title")
|
||||
for _, e := range doc.FindElementsPath(path) {
|
||||
fmt.Println(e.Text())
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
XQuery Kick Start
|
||||
```
|
||||
|
||||
Note that this example uses the FindElementsPath function, which takes as an
|
||||
argument a pre-compiled path object. Use precompiled paths when you plan to
|
||||
search with the same path more than once.
|
||||
|
||||
### Other features
|
||||
|
||||
These are just a few examples of the things the etree package can do. See the
|
||||
[documentation](http://godoc.org/github.com/beevik/etree) for a complete
|
||||
description of its capabilities.
|
||||
|
||||
### Contributing
|
||||
|
||||
This project accepts contributions. Just fork the repo and submit a pull
|
||||
request!
|
153
vendor/github.com/beevik/etree/RELEASE_NOTES.md
generated
vendored
Normal file
153
vendor/github.com/beevik/etree/RELEASE_NOTES.md
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
Release v1.2.0
|
||||
==============
|
||||
|
||||
**New Features**
|
||||
|
||||
* Add the ability to write XML fragments using Token WriteTo functions.
|
||||
* Add the ability to re-indent an XML element as though it were the root of
|
||||
the document.
|
||||
* Add a ReadSettings option to preserve CDATA blocks when reading and XML
|
||||
document.
|
||||
|
||||
Release v1.1.4
|
||||
==============
|
||||
|
||||
**New Features**
|
||||
|
||||
* Add the ability to preserve whitespace in leaf elements during indent.
|
||||
* Add the ability to suppress a document-trailing newline during indent.
|
||||
* Add choice of XML attribute quoting style (single-quote or double-quote).
|
||||
|
||||
**Removed Features**
|
||||
|
||||
* Removed the CDATA preservation change introduced in v1.1.3. It was
|
||||
implemented in a way that broke the ability to process XML documents
|
||||
encoded using non-UTF8 character sets.
|
||||
|
||||
Release v1.1.3
|
||||
==============
|
||||
|
||||
* XML reads now preserve CDATA sections instead of converting them to
|
||||
standard character data.
|
||||
|
||||
Release v1.1.2
|
||||
==============
|
||||
|
||||
* Fixed a path parsing bug.
|
||||
* The `Element.Text` function now handles comments embedded between
|
||||
character data spans.
|
||||
|
||||
Release v1.1.1
|
||||
==============
|
||||
|
||||
* Updated go version in `go.mod` to 1.20
|
||||
|
||||
Release v1.1.0
|
||||
==============
|
||||
|
||||
**New Features**
|
||||
|
||||
* New attribute helpers.
|
||||
* Added the `Element.SortAttrs` method, which lexicographically sorts an
|
||||
element's attributes by key.
|
||||
* New `ReadSettings` properties.
|
||||
* Added `Entity` for the support of custom entity maps.
|
||||
* New `WriteSettings` properties.
|
||||
* Added `UseCRLF` to allow the output of CR-LF newlines instead of the
|
||||
default LF newlines. This is useful on Windows systems.
|
||||
* Additional support for text and CDATA sections.
|
||||
* The `Element.Text` method now returns the concatenation of all consecutive
|
||||
character data tokens immediately following an element's opening tag.
|
||||
* Added `Element.SetCData` to replace the character data immediately
|
||||
following an element's opening tag with a CDATA section.
|
||||
* Added `Element.CreateCData` to create and add a CDATA section child
|
||||
`CharData` token to an element.
|
||||
* Added `Element.CreateText` to create and add a child text `CharData` token
|
||||
to an element.
|
||||
* Added `NewCData` to create a parentless CDATA section `CharData` token.
|
||||
* Added `NewText` to create a parentless text `CharData`
|
||||
token.
|
||||
* Added `CharData.IsCData` to detect if the token contains a CDATA section.
|
||||
* Added `CharData.IsWhitespace` to detect if the token contains whitespace
|
||||
inserted by one of the document Indent functions.
|
||||
* Modified `Element.SetText` so that it replaces a run of consecutive
|
||||
character data tokens following the element's opening tag (instead of just
|
||||
the first one).
|
||||
* New "tail text" support.
|
||||
* Added the `Element.Tail` method, which returns the text immediately
|
||||
following an element's closing tag.
|
||||
* Added the `Element.SetTail` method, which modifies the text immediately
|
||||
following an element's closing tag.
|
||||
* New element child insertion and removal methods.
|
||||
* Added the `Element.InsertChildAt` method, which inserts a new child token
|
||||
before the specified child token index.
|
||||
* Added the `Element.RemoveChildAt` method, which removes the child token at
|
||||
the specified child token index.
|
||||
* New element and attribute queries.
|
||||
* Added the `Element.Index` method, which returns the element's index within
|
||||
its parent element's child token list.
|
||||
* Added the `Element.NamespaceURI` method to return the namespace URI
|
||||
associated with an element.
|
||||
* Added the `Attr.NamespaceURI` method to return the namespace URI
|
||||
associated with an element.
|
||||
* Added the `Attr.Element` method to return the element that an attribute
|
||||
belongs to.
|
||||
* New Path filter functions.
|
||||
* Added `[local-name()='val']` to keep elements whose unprefixed tag matches
|
||||
the desired value.
|
||||
* Added `[name()='val']` to keep elements whose full tag matches the desired
|
||||
value.
|
||||
* Added `[namespace-prefix()='val']` to keep elements whose namespace prefix
|
||||
matches the desired value.
|
||||
* Added `[namespace-uri()='val']` to keep elements whose namespace URI
|
||||
matches the desired value.
|
||||
|
||||
**Bug Fixes**
|
||||
|
||||
* A default XML `CharSetReader` is now used to prevent failed parsing of XML
|
||||
documents using certain encodings.
|
||||
([Issue](https://github.com/beevik/etree/issues/53)).
|
||||
* All characters are now properly escaped according to XML parsing rules.
|
||||
([Issue](https://github.com/beevik/etree/issues/55)).
|
||||
* The `Document.Indent` and `Document.IndentTabs` functions no longer insert
|
||||
empty string `CharData` tokens.
|
||||
|
||||
**Deprecated**
|
||||
|
||||
* `Element`
|
||||
* The `InsertChild` method is deprecated. Use `InsertChildAt` instead.
|
||||
* The `CreateCharData` method is deprecated. Use `CreateText` instead.
|
||||
* `CharData`
|
||||
* The `NewCharData` method is deprecated. Use `NewText` instead.
|
||||
|
||||
|
||||
Release v1.0.1
|
||||
==============
|
||||
|
||||
**Changes**
|
||||
|
||||
* Added support for absolute etree Path queries. An absolute path begins with
|
||||
`/` or `//` and begins its search from the element's document root.
|
||||
* Added [`GetPath`](https://godoc.org/github.com/beevik/etree#Element.GetPath)
|
||||
and [`GetRelativePath`](https://godoc.org/github.com/beevik/etree#Element.GetRelativePath)
|
||||
functions to the [`Element`](https://godoc.org/github.com/beevik/etree#Element)
|
||||
type.
|
||||
|
||||
**Breaking changes**
|
||||
|
||||
* A path starting with `//` is now interpreted as an absolute path.
|
||||
Previously, it was interpreted as a relative path starting from the element
|
||||
whose
|
||||
[`FindElement`](https://godoc.org/github.com/beevik/etree#Element.FindElement)
|
||||
method was called. To remain compatible with this release, all paths
|
||||
prefixed with `//` should be prefixed with `.//` when called from any
|
||||
element other than the document's root.
|
||||
* [**edit 2/1/2019**]: Minor releases should not contain breaking changes.
|
||||
Even though this breaking change was very minor, it was a mistake to include
|
||||
it in this minor release. In the future, all breaking changes will be
|
||||
limited to major releases (e.g., version 2.0.0).
|
||||
|
||||
Release v1.0.0
|
||||
==============
|
||||
|
||||
Initial release.
|
1666
vendor/github.com/beevik/etree/etree.go
generated
vendored
Normal file
1666
vendor/github.com/beevik/etree/etree.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
394
vendor/github.com/beevik/etree/helpers.go
generated
vendored
Normal file
394
vendor/github.com/beevik/etree/helpers.go
generated
vendored
Normal file
@ -0,0 +1,394 @@
|
||||
// Copyright 2015-2019 Brett Vickers.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package etree
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A simple stack
|
||||
type stack struct {
|
||||
data []interface{}
|
||||
}
|
||||
|
||||
func (s *stack) empty() bool {
|
||||
return len(s.data) == 0
|
||||
}
|
||||
|
||||
func (s *stack) push(value interface{}) {
|
||||
s.data = append(s.data, value)
|
||||
}
|
||||
|
||||
func (s *stack) pop() interface{} {
|
||||
value := s.data[len(s.data)-1]
|
||||
s.data[len(s.data)-1] = nil
|
||||
s.data = s.data[:len(s.data)-1]
|
||||
return value
|
||||
}
|
||||
|
||||
func (s *stack) peek() interface{} {
|
||||
return s.data[len(s.data)-1]
|
||||
}
|
||||
|
||||
// A fifo is a simple first-in-first-out queue.
|
||||
type fifo struct {
|
||||
data []interface{}
|
||||
head, tail int
|
||||
}
|
||||
|
||||
func (f *fifo) add(value interface{}) {
|
||||
if f.len()+1 >= len(f.data) {
|
||||
f.grow()
|
||||
}
|
||||
f.data[f.tail] = value
|
||||
if f.tail++; f.tail == len(f.data) {
|
||||
f.tail = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fifo) remove() interface{} {
|
||||
value := f.data[f.head]
|
||||
f.data[f.head] = nil
|
||||
if f.head++; f.head == len(f.data) {
|
||||
f.head = 0
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func (f *fifo) len() int {
|
||||
if f.tail >= f.head {
|
||||
return f.tail - f.head
|
||||
}
|
||||
return len(f.data) - f.head + f.tail
|
||||
}
|
||||
|
||||
func (f *fifo) grow() {
|
||||
c := len(f.data) * 2
|
||||
if c == 0 {
|
||||
c = 4
|
||||
}
|
||||
buf, count := make([]interface{}, c), f.len()
|
||||
if f.tail >= f.head {
|
||||
copy(buf[0:count], f.data[f.head:f.tail])
|
||||
} else {
|
||||
hindex := len(f.data) - f.head
|
||||
copy(buf[0:hindex], f.data[f.head:])
|
||||
copy(buf[hindex:count], f.data[:f.tail])
|
||||
}
|
||||
f.data, f.head, f.tail = buf, 0, count
|
||||
}
|
||||
|
||||
// xmlReader provides the interface by which an XML byte stream is
|
||||
// processed and decoded.
|
||||
type xmlReader interface {
|
||||
Bytes() int64
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// xmlSimpleReader implements a proxy reader that counts the number of
|
||||
// bytes read from its encapsulated reader.
|
||||
type xmlSimpleReader struct {
|
||||
r io.Reader
|
||||
bytes int64
|
||||
}
|
||||
|
||||
func newXmlSimpleReader(r io.Reader) xmlReader {
|
||||
return &xmlSimpleReader{r, 0}
|
||||
}
|
||||
|
||||
func (xr *xmlSimpleReader) Bytes() int64 {
|
||||
return xr.bytes
|
||||
}
|
||||
|
||||
func (xr *xmlSimpleReader) Read(p []byte) (n int, err error) {
|
||||
n, err = xr.r.Read(p)
|
||||
xr.bytes += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// xmlPeekReader implements a proxy reader that counts the number of
|
||||
// bytes read from its encapsulated reader. It also allows the caller to
|
||||
// "peek" at the previous portions of the buffer after they have been
|
||||
// parsed.
|
||||
type xmlPeekReader struct {
|
||||
r io.Reader
|
||||
bytes int64 // total bytes read by the Read function
|
||||
buf []byte // internal read buffer
|
||||
bufSize int // total bytes used in the read buffer
|
||||
bufOffset int64 // total bytes read when buf was last filled
|
||||
window []byte // current read buffer window
|
||||
peekBuf []byte // buffer used to store data to be peeked at later
|
||||
peekOffset int64 // total read offset of the start of the peek buffer
|
||||
}
|
||||
|
||||
func newXmlPeekReader(r io.Reader) *xmlPeekReader {
|
||||
buf := make([]byte, 4096)
|
||||
return &xmlPeekReader{
|
||||
r: r,
|
||||
bytes: 0,
|
||||
buf: buf,
|
||||
bufSize: 0,
|
||||
bufOffset: 0,
|
||||
window: buf[0:0],
|
||||
peekBuf: make([]byte, 0),
|
||||
peekOffset: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) Bytes() int64 {
|
||||
return xr.bytes
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) Read(p []byte) (n int, err error) {
|
||||
if len(xr.window) == 0 {
|
||||
err = xr.fill()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(xr.window) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(xr.window) < len(p) {
|
||||
n = len(xr.window)
|
||||
} else {
|
||||
n = len(p)
|
||||
}
|
||||
|
||||
copy(p, xr.window)
|
||||
xr.window = xr.window[n:]
|
||||
xr.bytes += int64(n)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) PeekPrepare(offset int64, maxLen int) {
|
||||
if maxLen > cap(xr.peekBuf) {
|
||||
xr.peekBuf = make([]byte, 0, maxLen)
|
||||
}
|
||||
xr.peekBuf = xr.peekBuf[0:0]
|
||||
xr.peekOffset = offset
|
||||
xr.updatePeekBuf()
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) PeekFinalize() []byte {
|
||||
xr.updatePeekBuf()
|
||||
return xr.peekBuf
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) fill() error {
|
||||
xr.bufOffset = xr.bytes
|
||||
xr.bufSize = 0
|
||||
n, err := xr.r.Read(xr.buf)
|
||||
if err != nil {
|
||||
xr.window, xr.bufSize = xr.buf[0:0], 0
|
||||
return err
|
||||
}
|
||||
xr.window, xr.bufSize = xr.buf[:n], n
|
||||
xr.updatePeekBuf()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (xr *xmlPeekReader) updatePeekBuf() {
|
||||
peekRemain := cap(xr.peekBuf) - len(xr.peekBuf)
|
||||
if xr.peekOffset >= 0 && peekRemain > 0 {
|
||||
rangeMin := xr.peekOffset
|
||||
rangeMax := xr.peekOffset + int64(cap(xr.peekBuf))
|
||||
bufMin := xr.bufOffset
|
||||
bufMax := xr.bufOffset + int64(xr.bufSize)
|
||||
if rangeMin < bufMin {
|
||||
rangeMin = bufMin
|
||||
}
|
||||
if rangeMax > bufMax {
|
||||
rangeMax = bufMax
|
||||
}
|
||||
if rangeMax > rangeMin {
|
||||
rangeMin -= xr.bufOffset
|
||||
rangeMax -= xr.bufOffset
|
||||
if int(rangeMax-rangeMin) > peekRemain {
|
||||
rangeMax = rangeMin + int64(peekRemain)
|
||||
}
|
||||
xr.peekBuf = append(xr.peekBuf, xr.buf[rangeMin:rangeMax]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// xmlWriter implements a proxy writer that counts the number of
|
||||
// bytes written by its encapsulated writer.
|
||||
type xmlWriter struct {
|
||||
w io.Writer
|
||||
bytes int64
|
||||
}
|
||||
|
||||
func newXmlWriter(w io.Writer) *xmlWriter {
|
||||
return &xmlWriter{w: w}
|
||||
}
|
||||
|
||||
func (xw *xmlWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = xw.w.Write(p)
|
||||
xw.bytes += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// isWhitespace returns true if the byte slice contains only
|
||||
// whitespace characters.
|
||||
func isWhitespace(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if c := s[i]; c != ' ' && c != '\t' && c != '\n' && c != '\r' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// spaceMatch returns true if namespace a is the empty string
|
||||
// or if namespace a equals namespace b.
|
||||
func spaceMatch(a, b string) bool {
|
||||
switch {
|
||||
case a == "":
|
||||
return true
|
||||
default:
|
||||
return a == b
|
||||
}
|
||||
}
|
||||
|
||||
// spaceDecompose breaks a namespace:tag identifier at the ':'
|
||||
// and returns the two parts.
|
||||
func spaceDecompose(str string) (space, key string) {
|
||||
colon := strings.IndexByte(str, ':')
|
||||
if colon == -1 {
|
||||
return "", str
|
||||
}
|
||||
return str[:colon], str[colon+1:]
|
||||
}
|
||||
|
||||
// Strings used by indentCRLF and indentLF
|
||||
const (
|
||||
indentSpaces = "\r\n "
|
||||
indentTabs = "\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
|
||||
)
|
||||
|
||||
// indentCRLF returns a CRLF newline followed by n copies of the first
|
||||
// non-CRLF character in the source string.
|
||||
func indentCRLF(n int, source string) string {
|
||||
switch {
|
||||
case n < 0:
|
||||
return source[:2]
|
||||
case n < len(source)-1:
|
||||
return source[:n+2]
|
||||
default:
|
||||
return source + strings.Repeat(source[2:3], n-len(source)+2)
|
||||
}
|
||||
}
|
||||
|
||||
// indentLF returns a LF newline followed by n copies of the first non-LF
|
||||
// character in the source string.
|
||||
func indentLF(n int, source string) string {
|
||||
switch {
|
||||
case n < 0:
|
||||
return source[1:2]
|
||||
case n < len(source)-1:
|
||||
return source[1 : n+2]
|
||||
default:
|
||||
return source[1:] + strings.Repeat(source[2:3], n-len(source)+2)
|
||||
}
|
||||
}
|
||||
|
||||
// nextIndex returns the index of the next occurrence of sep in s,
|
||||
// starting from offset. It returns -1 if the sep string is not found.
|
||||
func nextIndex(s, sep string, offset int) int {
|
||||
switch i := strings.Index(s[offset:], sep); i {
|
||||
case -1:
|
||||
return -1
|
||||
default:
|
||||
return offset + i
|
||||
}
|
||||
}
|
||||
|
||||
// isInteger returns true if the string s contains an integer.
|
||||
func isInteger(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if (s[i] < '0' || s[i] > '9') && !(i == 0 && s[i] == '-') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type escapeMode byte
|
||||
|
||||
const (
|
||||
escapeNormal escapeMode = iota
|
||||
escapeCanonicalText
|
||||
escapeCanonicalAttr
|
||||
)
|
||||
|
||||
// escapeString writes an escaped version of a string to the writer.
|
||||
func escapeString(w Writer, s string, m escapeMode) {
|
||||
var esc []byte
|
||||
last := 0
|
||||
for i := 0; i < len(s); {
|
||||
r, width := utf8.DecodeRuneInString(s[i:])
|
||||
i += width
|
||||
switch r {
|
||||
case '&':
|
||||
esc = []byte("&")
|
||||
case '<':
|
||||
esc = []byte("<")
|
||||
case '>':
|
||||
if m == escapeCanonicalAttr {
|
||||
continue
|
||||
}
|
||||
esc = []byte(">")
|
||||
case '\'':
|
||||
if m != escapeNormal {
|
||||
continue
|
||||
}
|
||||
esc = []byte("'")
|
||||
case '"':
|
||||
if m == escapeCanonicalText {
|
||||
continue
|
||||
}
|
||||
esc = []byte(""")
|
||||
case '\t':
|
||||
if m != escapeCanonicalAttr {
|
||||
continue
|
||||
}
|
||||
esc = []byte("	")
|
||||
case '\n':
|
||||
if m != escapeCanonicalAttr {
|
||||
continue
|
||||
}
|
||||
esc = []byte("
")
|
||||
case '\r':
|
||||
if m == escapeNormal {
|
||||
continue
|
||||
}
|
||||
esc = []byte("
")
|
||||
default:
|
||||
if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
|
||||
esc = []byte("\uFFFD")
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
w.WriteString(s[last : i-width])
|
||||
w.Write(esc)
|
||||
last = i
|
||||
}
|
||||
w.WriteString(s[last:])
|
||||
}
|
||||
|
||||
func isInCharacterRange(r rune) bool {
|
||||
return r == 0x09 ||
|
||||
r == 0x0A ||
|
||||
r == 0x0D ||
|
||||
r >= 0x20 && r <= 0xD7FF ||
|
||||
r >= 0xE000 && r <= 0xFFFD ||
|
||||
r >= 0x10000 && r <= 0x10FFFF
|
||||
}
|
586
vendor/github.com/beevik/etree/path.go
generated
vendored
Normal file
586
vendor/github.com/beevik/etree/path.go
generated
vendored
Normal file
@ -0,0 +1,586 @@
|
||||
// Copyright 2015-2019 Brett Vickers.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package etree
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
A Path is a string that represents a search path through an etree starting
|
||||
from the document root or an arbitrary element. Paths are used with the
|
||||
Element object's Find* methods to locate and return desired elements.
|
||||
|
||||
A Path consists of a series of slash-separated "selectors", each of which may
|
||||
be modified by one or more bracket-enclosed "filters". Selectors are used to
|
||||
traverse the etree from element to element, while filters are used to narrow
|
||||
the list of candidate elements at each node.
|
||||
|
||||
Although etree Path strings are structurally and behaviorally similar to XPath
|
||||
strings (https://www.w3.org/TR/1999/REC-xpath-19991116/), they have a more
|
||||
limited set of selectors and filtering options.
|
||||
|
||||
The following selectors are supported by etree paths:
|
||||
|
||||
. Select the current element.
|
||||
.. Select the parent of the current element.
|
||||
* Select all child elements of the current element.
|
||||
/ Select the root element when used at the start of a path.
|
||||
// Select all descendants of the current element.
|
||||
tag Select all child elements with a name matching the tag.
|
||||
|
||||
The following basic filters are supported:
|
||||
|
||||
[@attrib] Keep elements with an attribute named attrib.
|
||||
[@attrib='val'] Keep elements with an attribute named attrib and value matching val.
|
||||
[tag] Keep elements with a child element named tag.
|
||||
[tag='val'] Keep elements with a child element named tag and text matching val.
|
||||
[n] Keep the n-th element, where n is a numeric index starting from 1.
|
||||
|
||||
The following function-based filters are supported:
|
||||
|
||||
[text()] Keep elements with non-empty text.
|
||||
[text()='val'] Keep elements whose text matches val.
|
||||
[local-name()='val'] Keep elements whose un-prefixed tag matches val.
|
||||
[name()='val'] Keep elements whose full tag exactly matches val.
|
||||
[namespace-prefix()] Keep elements with non-empty namespace prefixes.
|
||||
[namespace-prefix()='val'] Keep elements whose namespace prefix matches val.
|
||||
[namespace-uri()] Keep elements with non-empty namespace URIs.
|
||||
[namespace-uri()='val'] Keep elements whose namespace URI matches val.
|
||||
|
||||
Below are some examples of etree path strings.
|
||||
|
||||
Select the bookstore child element of the root element:
|
||||
|
||||
/bookstore
|
||||
|
||||
Beginning from the root element, select the title elements of all descendant
|
||||
book elements having a 'category' attribute of 'WEB':
|
||||
|
||||
//book[@category='WEB']/title
|
||||
|
||||
Beginning from the current element, select the first descendant book element
|
||||
with a title child element containing the text 'Great Expectations':
|
||||
|
||||
.//book[title='Great Expectations'][1]
|
||||
|
||||
Beginning from the current element, select all child elements of book elements
|
||||
with an attribute 'language' set to 'english':
|
||||
|
||||
./book/*[@language='english']
|
||||
|
||||
Beginning from the current element, select all child elements of book elements
|
||||
containing the text 'special':
|
||||
|
||||
./book/*[text()='special']
|
||||
|
||||
Beginning from the current element, select all descendant book elements whose
|
||||
title child element has a 'language' attribute of 'french':
|
||||
|
||||
.//book/title[@language='french']/..
|
||||
|
||||
Beginning from the current element, select all descendant book elements
|
||||
belonging to the http://www.w3.org/TR/html4/ namespace:
|
||||
|
||||
.//book[namespace-uri()='http://www.w3.org/TR/html4/']
|
||||
*/
|
||||
type Path struct {
|
||||
segments []segment
|
||||
}
|
||||
|
||||
// ErrPath is returned by path functions when an invalid etree path is provided.
|
||||
type ErrPath string
|
||||
|
||||
// Error returns the string describing a path error.
|
||||
func (err ErrPath) Error() string {
|
||||
return "etree: " + string(err)
|
||||
}
|
||||
|
||||
// CompilePath creates an optimized version of an XPath-like string that
|
||||
// can be used to query elements in an element tree.
|
||||
func CompilePath(path string) (Path, error) {
|
||||
var comp compiler
|
||||
segments := comp.parsePath(path)
|
||||
if comp.err != ErrPath("") {
|
||||
return Path{nil}, comp.err
|
||||
}
|
||||
return Path{segments}, nil
|
||||
}
|
||||
|
||||
// MustCompilePath creates an optimized version of an XPath-like string that
|
||||
// can be used to query elements in an element tree. Panics if an error
|
||||
// occurs. Use this function to create Paths when you know the path is
|
||||
// valid (i.e., if it's hard-coded).
|
||||
func MustCompilePath(path string) Path {
|
||||
p, err := CompilePath(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// A segment is a portion of a path between "/" characters.
|
||||
// It contains one selector and zero or more [filters].
|
||||
type segment struct {
|
||||
sel selector
|
||||
filters []filter
|
||||
}
|
||||
|
||||
func (seg *segment) apply(e *Element, p *pather) {
|
||||
seg.sel.apply(e, p)
|
||||
for _, f := range seg.filters {
|
||||
f.apply(p)
|
||||
}
|
||||
}
|
||||
|
||||
// A selector selects XML elements for consideration by the
|
||||
// path traversal.
|
||||
type selector interface {
|
||||
apply(e *Element, p *pather)
|
||||
}
|
||||
|
||||
// A filter pares down a list of candidate XML elements based
|
||||
// on a path filter in [brackets].
|
||||
type filter interface {
|
||||
apply(p *pather)
|
||||
}
|
||||
|
||||
// A pather is helper object that traverses an element tree using
|
||||
// a Path object. It collects and deduplicates all elements matching
|
||||
// the path query.
|
||||
type pather struct {
|
||||
queue fifo
|
||||
results []*Element
|
||||
inResults map[*Element]bool
|
||||
candidates []*Element
|
||||
scratch []*Element // used by filters
|
||||
}
|
||||
|
||||
// A node represents an element and the remaining path segments that
|
||||
// should be applied against it by the pather.
|
||||
type node struct {
|
||||
e *Element
|
||||
segments []segment
|
||||
}
|
||||
|
||||
func newPather() *pather {
|
||||
return &pather{
|
||||
results: make([]*Element, 0),
|
||||
inResults: make(map[*Element]bool),
|
||||
candidates: make([]*Element, 0),
|
||||
scratch: make([]*Element, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// traverse follows the path from the element e, collecting
|
||||
// and then returning all elements that match the path's selectors
|
||||
// and filters.
|
||||
func (p *pather) traverse(e *Element, path Path) []*Element {
|
||||
for p.queue.add(node{e, path.segments}); p.queue.len() > 0; {
|
||||
p.eval(p.queue.remove().(node))
|
||||
}
|
||||
return p.results
|
||||
}
|
||||
|
||||
// eval evaluates the current path node by applying the remaining
|
||||
// path's selector rules against the node's element.
|
||||
func (p *pather) eval(n node) {
|
||||
p.candidates = p.candidates[0:0]
|
||||
seg, remain := n.segments[0], n.segments[1:]
|
||||
seg.apply(n.e, p)
|
||||
|
||||
if len(remain) == 0 {
|
||||
for _, c := range p.candidates {
|
||||
if in := p.inResults[c]; !in {
|
||||
p.inResults[c] = true
|
||||
p.results = append(p.results, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, c := range p.candidates {
|
||||
p.queue.add(node{c, remain})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A compiler generates a compiled path from a path string.
|
||||
type compiler struct {
|
||||
err ErrPath
|
||||
}
|
||||
|
||||
// parsePath parses an XPath-like string describing a path
|
||||
// through an element tree and returns a slice of segment
|
||||
// descriptors.
|
||||
func (c *compiler) parsePath(path string) []segment {
|
||||
// If path ends with //, fix it
|
||||
if strings.HasSuffix(path, "//") {
|
||||
path += "*"
|
||||
}
|
||||
|
||||
var segments []segment
|
||||
|
||||
// Check for an absolute path
|
||||
if strings.HasPrefix(path, "/") {
|
||||
segments = append(segments, segment{new(selectRoot), []filter{}})
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
// Split path into segments
|
||||
for _, s := range splitPath(path) {
|
||||
segments = append(segments, c.parseSegment(s))
|
||||
if c.err != ErrPath("") {
|
||||
break
|
||||
}
|
||||
}
|
||||
return segments
|
||||
}
|
||||
|
||||
func splitPath(path string) []string {
|
||||
var pieces []string
|
||||
start := 0
|
||||
inquote := false
|
||||
for i := 0; i+1 <= len(path); i++ {
|
||||
if path[i] == '\'' {
|
||||
inquote = !inquote
|
||||
} else if path[i] == '/' && !inquote {
|
||||
pieces = append(pieces, path[start:i])
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
return append(pieces, path[start:])
|
||||
}
|
||||
|
||||
// parseSegment parses a path segment between / characters.
|
||||
func (c *compiler) parseSegment(path string) segment {
|
||||
pieces := strings.Split(path, "[")
|
||||
seg := segment{
|
||||
sel: c.parseSelector(pieces[0]),
|
||||
filters: []filter{},
|
||||
}
|
||||
for i := 1; i < len(pieces); i++ {
|
||||
fpath := pieces[i]
|
||||
if len(fpath) == 0 || fpath[len(fpath)-1] != ']' {
|
||||
c.err = ErrPath("path has invalid filter [brackets].")
|
||||
break
|
||||
}
|
||||
seg.filters = append(seg.filters, c.parseFilter(fpath[:len(fpath)-1]))
|
||||
}
|
||||
return seg
|
||||
}
|
||||
|
||||
// parseSelector parses a selector at the start of a path segment.
|
||||
func (c *compiler) parseSelector(path string) selector {
|
||||
switch path {
|
||||
case ".":
|
||||
return new(selectSelf)
|
||||
case "..":
|
||||
return new(selectParent)
|
||||
case "*":
|
||||
return new(selectChildren)
|
||||
case "":
|
||||
return new(selectDescendants)
|
||||
default:
|
||||
return newSelectChildrenByTag(path)
|
||||
}
|
||||
}
|
||||
|
||||
var fnTable = map[string]func(e *Element) string{
|
||||
"local-name": (*Element).name,
|
||||
"name": (*Element).FullTag,
|
||||
"namespace-prefix": (*Element).namespacePrefix,
|
||||
"namespace-uri": (*Element).NamespaceURI,
|
||||
"text": (*Element).Text,
|
||||
}
|
||||
|
||||
// parseFilter parses a path filter contained within [brackets].
|
||||
func (c *compiler) parseFilter(path string) filter {
|
||||
if len(path) == 0 {
|
||||
c.err = ErrPath("path contains an empty filter expression.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter contains [@attr='val'], [fn()='val'], or [tag='val']?
|
||||
eqindex := strings.Index(path, "='")
|
||||
if eqindex >= 0 {
|
||||
rindex := nextIndex(path, "'", eqindex+2)
|
||||
if rindex != len(path)-1 {
|
||||
c.err = ErrPath("path has mismatched filter quotes.")
|
||||
return nil
|
||||
}
|
||||
|
||||
key := path[:eqindex]
|
||||
value := path[eqindex+2 : rindex]
|
||||
|
||||
switch {
|
||||
case key[0] == '@':
|
||||
return newFilterAttrVal(key[1:], value)
|
||||
case strings.HasSuffix(key, "()"):
|
||||
name := key[:len(key)-2]
|
||||
if fn, ok := fnTable[name]; ok {
|
||||
return newFilterFuncVal(fn, value)
|
||||
}
|
||||
c.err = ErrPath("path has unknown function " + name)
|
||||
return nil
|
||||
default:
|
||||
return newFilterChildText(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter contains [@attr], [N], [tag] or [fn()]
|
||||
switch {
|
||||
case path[0] == '@':
|
||||
return newFilterAttr(path[1:])
|
||||
case strings.HasSuffix(path, "()"):
|
||||
name := path[:len(path)-2]
|
||||
if fn, ok := fnTable[name]; ok {
|
||||
return newFilterFunc(fn)
|
||||
}
|
||||
c.err = ErrPath("path has unknown function " + name)
|
||||
return nil
|
||||
case isInteger(path):
|
||||
pos, _ := strconv.Atoi(path)
|
||||
switch {
|
||||
case pos > 0:
|
||||
return newFilterPos(pos - 1)
|
||||
default:
|
||||
return newFilterPos(pos)
|
||||
}
|
||||
default:
|
||||
return newFilterChild(path)
|
||||
}
|
||||
}
|
||||
|
||||
// selectSelf selects the current element into the candidate list.
|
||||
type selectSelf struct{}
|
||||
|
||||
func (s *selectSelf) apply(e *Element, p *pather) {
|
||||
p.candidates = append(p.candidates, e)
|
||||
}
|
||||
|
||||
// selectRoot selects the element's root node.
|
||||
type selectRoot struct{}
|
||||
|
||||
func (s *selectRoot) apply(e *Element, p *pather) {
|
||||
root := e
|
||||
for root.parent != nil {
|
||||
root = root.parent
|
||||
}
|
||||
p.candidates = append(p.candidates, root)
|
||||
}
|
||||
|
||||
// selectParent selects the element's parent into the candidate list.
|
||||
type selectParent struct{}
|
||||
|
||||
func (s *selectParent) apply(e *Element, p *pather) {
|
||||
if e.parent != nil {
|
||||
p.candidates = append(p.candidates, e.parent)
|
||||
}
|
||||
}
|
||||
|
||||
// selectChildren selects the element's child elements into the
|
||||
// candidate list.
|
||||
type selectChildren struct{}
|
||||
|
||||
func (s *selectChildren) apply(e *Element, p *pather) {
|
||||
for _, c := range e.Child {
|
||||
if c, ok := c.(*Element); ok {
|
||||
p.candidates = append(p.candidates, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectDescendants selects all descendant child elements
|
||||
// of the element into the candidate list.
|
||||
type selectDescendants struct{}
|
||||
|
||||
func (s *selectDescendants) apply(e *Element, p *pather) {
|
||||
var queue fifo
|
||||
for queue.add(e); queue.len() > 0; {
|
||||
e := queue.remove().(*Element)
|
||||
p.candidates = append(p.candidates, e)
|
||||
for _, c := range e.Child {
|
||||
if c, ok := c.(*Element); ok {
|
||||
queue.add(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectChildrenByTag selects into the candidate list all child
|
||||
// elements of the element having the specified tag.
|
||||
type selectChildrenByTag struct {
|
||||
space, tag string
|
||||
}
|
||||
|
||||
func newSelectChildrenByTag(path string) *selectChildrenByTag {
|
||||
s, l := spaceDecompose(path)
|
||||
return &selectChildrenByTag{s, l}
|
||||
}
|
||||
|
||||
func (s *selectChildrenByTag) apply(e *Element, p *pather) {
|
||||
for _, c := range e.Child {
|
||||
if c, ok := c.(*Element); ok && spaceMatch(s.space, c.Space) && s.tag == c.Tag {
|
||||
p.candidates = append(p.candidates, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filterPos filters the candidate list, keeping only the
|
||||
// candidate at the specified index.
|
||||
type filterPos struct {
|
||||
index int
|
||||
}
|
||||
|
||||
func newFilterPos(pos int) *filterPos {
|
||||
return &filterPos{pos}
|
||||
}
|
||||
|
||||
func (f *filterPos) apply(p *pather) {
|
||||
if f.index >= 0 {
|
||||
if f.index < len(p.candidates) {
|
||||
p.scratch = append(p.scratch, p.candidates[f.index])
|
||||
}
|
||||
} else {
|
||||
if -f.index <= len(p.candidates) {
|
||||
p.scratch = append(p.scratch, p.candidates[len(p.candidates)+f.index])
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterAttr filters the candidate list for elements having
|
||||
// the specified attribute.
|
||||
type filterAttr struct {
|
||||
space, key string
|
||||
}
|
||||
|
||||
func newFilterAttr(str string) *filterAttr {
|
||||
s, l := spaceDecompose(str)
|
||||
return &filterAttr{s, l}
|
||||
}
|
||||
|
||||
func (f *filterAttr) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
for _, a := range c.Attr {
|
||||
if spaceMatch(f.space, a.Space) && f.key == a.Key {
|
||||
p.scratch = append(p.scratch, c)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterAttrVal filters the candidate list for elements having
|
||||
// the specified attribute with the specified value.
|
||||
type filterAttrVal struct {
|
||||
space, key, val string
|
||||
}
|
||||
|
||||
func newFilterAttrVal(str, value string) *filterAttrVal {
|
||||
s, l := spaceDecompose(str)
|
||||
return &filterAttrVal{s, l, value}
|
||||
}
|
||||
|
||||
func (f *filterAttrVal) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
for _, a := range c.Attr {
|
||||
if spaceMatch(f.space, a.Space) && f.key == a.Key && f.val == a.Value {
|
||||
p.scratch = append(p.scratch, c)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterFunc filters the candidate list for elements satisfying a custom
|
||||
// boolean function.
|
||||
type filterFunc struct {
|
||||
fn func(e *Element) string
|
||||
}
|
||||
|
||||
func newFilterFunc(fn func(e *Element) string) *filterFunc {
|
||||
return &filterFunc{fn}
|
||||
}
|
||||
|
||||
func (f *filterFunc) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
if f.fn(c) != "" {
|
||||
p.scratch = append(p.scratch, c)
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterFuncVal filters the candidate list for elements containing a value
|
||||
// matching the result of a custom function.
|
||||
type filterFuncVal struct {
|
||||
fn func(e *Element) string
|
||||
val string
|
||||
}
|
||||
|
||||
func newFilterFuncVal(fn func(e *Element) string, value string) *filterFuncVal {
|
||||
return &filterFuncVal{fn, value}
|
||||
}
|
||||
|
||||
func (f *filterFuncVal) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
if f.fn(c) == f.val {
|
||||
p.scratch = append(p.scratch, c)
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterChild filters the candidate list for elements having
|
||||
// a child element with the specified tag.
|
||||
type filterChild struct {
|
||||
space, tag string
|
||||
}
|
||||
|
||||
func newFilterChild(str string) *filterChild {
|
||||
s, l := spaceDecompose(str)
|
||||
return &filterChild{s, l}
|
||||
}
|
||||
|
||||
func (f *filterChild) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
for _, cc := range c.Child {
|
||||
if cc, ok := cc.(*Element); ok &&
|
||||
spaceMatch(f.space, cc.Space) &&
|
||||
f.tag == cc.Tag {
|
||||
p.scratch = append(p.scratch, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
||||
|
||||
// filterChildText filters the candidate list for elements having
|
||||
// a child element with the specified tag and text.
|
||||
type filterChildText struct {
|
||||
space, tag, text string
|
||||
}
|
||||
|
||||
func newFilterChildText(str, text string) *filterChildText {
|
||||
s, l := spaceDecompose(str)
|
||||
return &filterChildText{s, l, text}
|
||||
}
|
||||
|
||||
func (f *filterChildText) apply(p *pather) {
|
||||
for _, c := range p.candidates {
|
||||
for _, cc := range c.Child {
|
||||
if cc, ok := cc.(*Element); ok &&
|
||||
spaceMatch(f.space, cc.Space) &&
|
||||
f.tag == cc.Tag &&
|
||||
f.text == cc.Text() {
|
||||
p.scratch = append(p.scratch, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
p.candidates, p.scratch = p.scratch, p.candidates[0:0]
|
||||
}
|
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
25
vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
.idea/
|
||||
*.iml
|
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
9
vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
22
vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
39
vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
39
vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# Gorilla WebSocket
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
||||
[![CircleCI](https://circleci.com/gh/gorilla/websocket.svg?style=svg)](https://circleci.com/gh/gorilla/websocket)
|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
|
||||
---
|
||||
|
||||
⚠️ **[The Gorilla WebSocket Package is looking for a new maintainer](https://github.com/gorilla/websocket/issues/370)**
|
||||
|
||||
---
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
|
||||
### Status
|
||||
|
||||
The Gorilla WebSocket package provides a complete and tested implementation of
|
||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
|
||||
package API is stable.
|
||||
|
||||
### Installation
|
||||
|
||||
go get github.com/gorilla/websocket
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
422
vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
422
vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
// (Cookie). Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etc.
|
||||
//
|
||||
// Deprecated: Use Dialer instead.
|
||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
|
||||
d := Dialer{
|
||||
ReadBufferSize: readBufSize,
|
||||
WriteBufferSize: writeBufSize,
|
||||
NetDial: func(net, addr string) (net.Conn, error) {
|
||||
return netConn, nil
|
||||
},
|
||||
}
|
||||
return d.Dial(u.String(), requestHeader)
|
||||
}
|
||||
|
||||
// A Dialer contains options for connecting to WebSocket server.
|
||||
//
|
||||
// It is safe to call Dialer's methods concurrently.
|
||||
type Dialer struct {
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialContext specifies the dial function for creating TCP connections. If
|
||||
// NetDialContext is nil, NetDial is used.
|
||||
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
|
||||
// NetDialTLSContext is nil, NetDialContext is used.
|
||||
// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
|
||||
// TLSClientConfig is ignored.
|
||||
NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
// If Proxy is nil or returns a nil *URL, no proxy is used.
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
|
||||
// is done there and TLSClientConfig is ignored.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
|
||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||
// do not limit the size of the messages that can be sent or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// WriteBufferPool is a pool of buffers for write operations. If the value
|
||||
// is not set, then write buffers are allocated to the connection for the
|
||||
// lifetime of the connection.
|
||||
//
|
||||
// A pool is most useful when the application has a modest volume of writes
|
||||
// across a large number of connections.
|
||||
//
|
||||
// Applications should use a single pool for each unique value of
|
||||
// WriteBufferSize.
|
||||
WriteBufferPool BufferPool
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
// Dial creates a new client connection by calling DialContext with a background context.
|
||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
return d.DialContext(context.Background(), urlStr, requestHeader)
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
|
||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||
hostPort = u.Host
|
||||
hostNoPort = u.Host
|
||||
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
|
||||
hostNoPort = hostNoPort[:i]
|
||||
} else {
|
||||
switch u.Scheme {
|
||||
case "wss":
|
||||
hostPort += ":443"
|
||||
case "https":
|
||||
hostPort += ":443"
|
||||
default:
|
||||
hostPort += ":80"
|
||||
}
|
||||
}
|
||||
return hostPort, hostNoPort
|
||||
}
|
||||
|
||||
// DefaultDialer is a dialer with all fields set to the default values.
|
||||
var DefaultDialer = &Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
}
|
||||
|
||||
// nilDialer is dialer to use when receiver is nil.
|
||||
var nilDialer = *DefaultDialer
|
||||
|
||||
// DialContext creates a new client connection. Use requestHeader to specify the
|
||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
|
||||
// Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// The context will be used in the request and in the Dialer.
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etcetera. The response body may not contain the entire response and does not
|
||||
// need to be closed by the application.
|
||||
func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
if d == nil {
|
||||
d = &nilDialer
|
||||
}
|
||||
|
||||
challengeKey, err := generateChallengeKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
// User name and password are not allowed in websocket URIs.
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: u,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
Host: u.Host,
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||
// servers that depend on it. The Header.Set method is not used because the
|
||||
// method canonicalizes the header names.
|
||||
req.Header["Upgrade"] = []string{"websocket"}
|
||||
req.Header["Connection"] = []string{"Upgrade"}
|
||||
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
|
||||
req.Header["Sec-WebSocket-Version"] = []string{"13"}
|
||||
if len(d.Subprotocols) > 0 {
|
||||
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
|
||||
}
|
||||
for k, vs := range requestHeader {
|
||||
switch {
|
||||
case k == "Host":
|
||||
if len(vs) > 0 {
|
||||
req.Host = vs[0]
|
||||
}
|
||||
case k == "Upgrade" ||
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
case k == "Sec-Websocket-Protocol":
|
||||
req.Header["Sec-WebSocket-Protocol"] = vs
|
||||
default:
|
||||
req.Header[k] = vs
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
|
||||
}
|
||||
|
||||
if d.HandshakeTimeout != 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Get network dial function.
|
||||
var netDial func(network, add string) (net.Conn, error)
|
||||
|
||||
switch u.Scheme {
|
||||
case "http":
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
case "https":
|
||||
if d.NetDialTLSContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialTLSContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{}
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
forwardDial := netDial
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
c, err := forwardDial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to connect through a proxy.
|
||||
if d.Proxy != nil {
|
||||
proxyURL, err := d.Proxy(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if proxyURL != nil {
|
||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
netDial = dialer.Dial
|
||||
}
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
trace := httptrace.ContextClientTrace(ctx)
|
||||
if trace != nil && trace.GetConn != nil {
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(httptrace.GotConnInfo{
|
||||
Conn: netConn,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if u.Scheme == "https" && d.NetDialTLSContext == nil {
|
||||
// If NetDialTLSContext is set, assume that the TLS handshake has already been done
|
||||
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
netConn = tlsConn
|
||||
|
||||
if trace != nil && trace.TLSHandshakeStart != nil {
|
||||
trace.TLSHandshakeStart()
|
||||
}
|
||||
err := doHandshake(ctx, tlsConn, cfg)
|
||||
if trace != nil && trace.TLSHandshakeDone != nil {
|
||||
trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)
|
||||
|
||||
if err := req.Write(netConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if trace != nil && trace.GotFirstResponseByte != nil {
|
||||
if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
|
||||
trace.GotFirstResponseByte()
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||
|
||||
!tokenListContainsValue(resp.Header, "Connection", "upgrade") ||
|
||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
||||
// Before closing the network connection on return from this
|
||||
// function, slurp up some of the response to aid application
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(resp.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user