Restored progress
This commit is contained in:
parent
f615cff76d
commit
5244f87411
22
.vscode/tasks.json
vendored
Normal file
22
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Go run file",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "go run ${file}",
|
||||||
|
"args": [],
|
||||||
|
"group": "none",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": true,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": false,
|
||||||
|
"clear": true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
44
cmd/client/main.go
Normal file
44
cmd/client/main.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cosync/internal/client"
|
||||||
|
"cosync/internal/server"
|
||||||
|
"cosync/tests"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO: handle arguments correctly
|
||||||
|
func main() {
|
||||||
|
argc := len(os.Args)
|
||||||
|
if argc < 2 {
|
||||||
|
panic("Not enough arguments (try --help)")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for i := 1; i < argc; i++ {
|
||||||
|
|
||||||
|
switch (os.Args[i]) {
|
||||||
|
|
||||||
|
case "-s":
|
||||||
|
server.Run()
|
||||||
|
// break
|
||||||
|
|
||||||
|
case "-c":
|
||||||
|
client.Run()
|
||||||
|
// break
|
||||||
|
|
||||||
|
case "-p":
|
||||||
|
panic("Not implemented")
|
||||||
|
|
||||||
|
case "--help":
|
||||||
|
panic("Not implemented")
|
||||||
|
|
||||||
|
case "--test":
|
||||||
|
tests.TestAll()
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("Bad argument : \"" + os.Args[i] + "\"")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
cmd/server/main.go
Normal file
1
cmd/server/main.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package server
|
26
go.mod
Normal file
26
go.mod
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module cosync
|
||||||
|
|
||||||
|
go 1.22.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.19.0 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
|
golang.org/x/sys v0.18.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
54
go.sum
Normal file
54
go.sum
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
|
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
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=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
42
internal/client/client.go
Normal file
42
internal/client/client.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"cosync/internal/core"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO: handle errors, review logging
|
||||||
|
func Run() {
|
||||||
|
|
||||||
|
log.Println("Loading config")
|
||||||
|
|
||||||
|
// Loading config into program
|
||||||
|
err := core.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
config := core.GetConfig()
|
||||||
|
|
||||||
|
log.Println("Loaded")
|
||||||
|
|
||||||
|
serverAddress := config["server"].(string)
|
||||||
|
serverPort := strconv.Itoa(config["port"].(int))
|
||||||
|
|
||||||
|
log.Println("Connecting to server")
|
||||||
|
|
||||||
|
// Connect to the server
|
||||||
|
conn, err := net.Dial("tcp", serverAddress + ":" + serverPort)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
log.Println("Connected")
|
||||||
|
|
||||||
|
conn.Write([]byte("Hello server"))
|
||||||
|
}
|
104
internal/client/setup.go
Normal file
104
internal/client/setup.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cosync/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorRed = "\033[0;31m"
|
||||||
|
colorGreen = "\033[1;32m"
|
||||||
|
colorNone = "\033[0m"
|
||||||
|
separator = "\n ============================================== \n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Setup() {
|
||||||
|
|
||||||
|
// Print logo
|
||||||
|
println(" ______ ______ \n .' ___ | .' ____ \\ \n/ .' \\_| .--. | (___ \\_| _ __ _ .--. .---. \n| | / .'`\\ \\ _.____`. [ \\ [ ][ `.-. | / /'`\\] \n\\ `.___.'\\| \\__. || \\____) | \\ '/ / | | | | | \\__. \n `.____ .' '.__.' \\______.'[\\_: / [___||__]'.___.' \n \\__.' ")
|
||||||
|
print(separator)
|
||||||
|
|
||||||
|
// Greeting
|
||||||
|
println("Hi, welcome to cosync, let's set it up")
|
||||||
|
println("Don't hesitate to give a look at the doc to know how it works ! :) \n -> https://docs.oblic-parallels.fr/cosync\n")
|
||||||
|
|
||||||
|
// Try create config files
|
||||||
|
err := core.MkConfig()
|
||||||
|
if err != nil {
|
||||||
|
println(colorRed + "Setup: Failed to create config files" + colorNone)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for server/port assignation
|
||||||
|
print("Server address: ")
|
||||||
|
// ...
|
||||||
|
print("Port")
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Update config file accordingly
|
||||||
|
// ...
|
||||||
|
println(colorGreen + "Config has been updated with new values" + colorNone)
|
||||||
|
|
||||||
|
// Prompt to start service
|
||||||
|
print("Do you want to start CoSync now ? [Y/n]: ")
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
|
||||||
|
//! This code no longer needs to exist as config is now handled by cosync/config
|
||||||
|
// Then it will be removed soon
|
||||||
|
|
||||||
|
// func mkConfigFiles(configFolderPath string, configFileName string, syncFileName string) {
|
||||||
|
|
||||||
|
// // Check if folder already exists
|
||||||
|
// _, err := os.Stat(configFolderPath)
|
||||||
|
// if err != nil && os.IsNotExist(err) {
|
||||||
|
// // Create config folder
|
||||||
|
// err = os.Mkdir(configFolderPath, os.ModePerm)
|
||||||
|
// if err != nil {
|
||||||
|
// println(colorRed + "\n\nCouldn't create the config folder" + colorNone)
|
||||||
|
// panic("Setup: Couldn't create " + configFolderPath + " -> " + err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Check if config file exists
|
||||||
|
// configFilePath := configFolderPath + configFileName
|
||||||
|
// _, err = os.Stat(configFilePath)
|
||||||
|
// if err != nil && os.IsNotExist(err) {
|
||||||
|
// // Create config file
|
||||||
|
// _, err = os.Create(configFilePath)
|
||||||
|
// if err != nil {
|
||||||
|
// println(colorRed + "\n\nCouldn't create the config file" + colorNone)
|
||||||
|
// panic("Setup: Couldn't create " + configFilePath + " -> " + err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// syncFilePath := configFolderPath + syncFileName
|
||||||
|
// _, err = os.Stat(syncFilePath)
|
||||||
|
// if err != nil && os.IsNotExist(err) {
|
||||||
|
// // Create config folder
|
||||||
|
// _, err = os.Create(syncFilePath)
|
||||||
|
// if err != nil {
|
||||||
|
// println(colorRed + "\n\nCouldn't create the sync file" + colorNone)
|
||||||
|
// panic("Setup: Couldn't create " + syncFilePath + " -> " + err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func WriteDefaultConfig(configFilePath string) {
|
||||||
|
|
||||||
|
// // Open file
|
||||||
|
// f, err := os.Open(configFilePath)
|
||||||
|
// if err != nil {
|
||||||
|
// println(colorRed + "\n\nCouldn't open the config file" + colorNone)
|
||||||
|
// panic("Setup: Couldn't access " + configFilePath + " -> " + err.Error())
|
||||||
|
// }
|
||||||
|
// defer f.Close()
|
||||||
|
|
||||||
|
// // Write file
|
||||||
|
// // TODO: import pre existing config file
|
||||||
|
// _, err = f.WriteString("{}")
|
||||||
|
// if err != nil {
|
||||||
|
// println(colorRed + "\n\nCouldn't open the config file" + colorNone)
|
||||||
|
// panic("Setup: Couldn't access " + configFilePath + " -> " + err.Error())
|
||||||
|
// }
|
||||||
|
// }
|
63
internal/core/config.go
Normal file
63
internal/core/config.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Config will follow this model
|
||||||
|
// server : string
|
||||||
|
// port : int
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Load config file
|
||||||
|
func LoadConfig() error {
|
||||||
|
|
||||||
|
// Get current user
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
// panic("LoadConfig: Failed to retrieve curent user -> " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default values
|
||||||
|
viper.SetDefault("server", "example.com")
|
||||||
|
viper.SetDefault("port", 5660)
|
||||||
|
|
||||||
|
// Set config paths
|
||||||
|
viper.SetConfigName("config")
|
||||||
|
viper.SetConfigType("json")
|
||||||
|
viper.AddConfigPath("/etc/cosync/")
|
||||||
|
viper.AddConfigPath("/home/" + currentUser.Username + "/.config/cosync")
|
||||||
|
|
||||||
|
// Read from config files
|
||||||
|
err = viper.ReadInConfig()
|
||||||
|
if err != nil {
|
||||||
|
panic("LoadConfig: Failed to read config file -> " + err.Error())
|
||||||
|
// return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate config folder and files
|
||||||
|
func MkConfig() error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sync list from sync file
|
||||||
|
func LoadSyncList() error {
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns all config variables
|
||||||
|
// ! WARNING: Do not use this function without executing LoadConfig prior
|
||||||
|
func GetConfig() map[string]any {
|
||||||
|
return viper.AllSettings()
|
||||||
|
}
|
25
internal/core/models.go
Normal file
25
internal/core/models.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
ID string
|
||||||
|
ParentDirectory string
|
||||||
|
Metadata FileMetadata
|
||||||
|
State SynchronizationState
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileMetadata struct {
|
||||||
|
Name string
|
||||||
|
Size int64
|
||||||
|
ModifiedTime time.Time
|
||||||
|
Hash string // For comparing file content
|
||||||
|
}
|
||||||
|
|
||||||
|
type SynchronizationState struct {
|
||||||
|
Status string // "pending", "in_progress", "completed", "error"
|
||||||
|
Progress float64
|
||||||
|
LastError error
|
||||||
|
}
|
8
internal/core/utils.go
Normal file
8
internal/core/utils.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
|
||||||
|
//! Not implemented
|
||||||
|
func CompareFiles(file1 File, file2 File) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
83
internal/server/server.go
Normal file
83
internal/server/server.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"cosync/internal/core"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: handle errors, review logging
|
||||||
|
func Run() {
|
||||||
|
|
||||||
|
log.Println("Loading config")
|
||||||
|
|
||||||
|
// Loading config into program
|
||||||
|
err := core.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
config := core.GetConfig()
|
||||||
|
|
||||||
|
log.Println("Loaded")
|
||||||
|
|
||||||
|
serverPort := strconv.Itoa(config["port"].(int))
|
||||||
|
|
||||||
|
// Listening
|
||||||
|
listener, err := net.Listen("tcp", ":" + serverPort)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
log.Println("Server listening on port " + serverPort)
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error accepting connection:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConnection(conn net.Conn) {
|
||||||
|
|
||||||
|
// tells the connector to close once exchange ends
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
clientAddress := conn.RemoteAddr().String()
|
||||||
|
log.Println("Accepting from ", clientAddress)
|
||||||
|
|
||||||
|
// Read the message sent by the client (line by line)
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
for {
|
||||||
|
message, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
log.Println(clientAddress, " disconnected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Error reading from ", clientAddress, ": ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// to rm
|
||||||
|
println(message)
|
||||||
|
// fmt.Println("Received from client:", message)
|
||||||
|
|
||||||
|
// Process the message (e.g., send a response)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Send a response to the client
|
||||||
|
_, err = conn.Write([]byte("Server received your message\n"))
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error replying to", clientAddress,": ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
internal/server/setup.go
Normal file
1
internal/server/setup.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package server
|
74
lab/TODO.md
Normal file
74
lab/TODO.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
- Implement a core package that will be in charge of file comparison, transfer
|
||||||
|
|
||||||
|
- Implement protocol :
|
||||||
|
- Be careful about metadata, large files, error handling and performance
|
||||||
|
- Implement the sql version
|
||||||
|
- Implement the tls version
|
||||||
|
|
||||||
|
- Create configuration file :
|
||||||
|
- server address
|
||||||
|
- sync mode
|
||||||
|
- sync interval
|
||||||
|
|
||||||
|
- Enhanced error handling :
|
||||||
|
- Only use panic during uncoverable situations
|
||||||
|
- Use more explicit error types
|
||||||
|
|
||||||
|
- Use json/yaml config format (see viper)
|
||||||
|
|
||||||
|
- Authentication :
|
||||||
|
- Use a token based authentication system
|
||||||
|
- Use a secure way to store the token (KMS ?)
|
||||||
|
|
||||||
|
- Maintain a data structure to track the synchronization state of files :
|
||||||
|
- Local status
|
||||||
|
- Remote status
|
||||||
|
- Synchronization status
|
||||||
|
|
||||||
|
- See io/ioutil or io/fs to read/write files
|
||||||
|
|
||||||
|
- Break functions into little functions
|
||||||
|
|
||||||
|
- Use appropriate data structures to represent files and folders. Consider using a map to store file information for faster lookups.
|
||||||
|
|
||||||
|
- Improve logging (using a library like logrus or zap) ?
|
||||||
|
|
||||||
|
- Unit & integration tests
|
||||||
|
|
||||||
|
- Improve code style
|
||||||
|
|
||||||
|
- Document
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Gemini
|
||||||
|
|
||||||
|
- Implement a CLI :
|
||||||
|
- Use a library like cobra or urfave/cli
|
||||||
|
|
||||||
|
- Implement a GUI :
|
||||||
|
- Use a library like fltk or qt
|
||||||
|
|
||||||
|
- Implement a web interface :
|
||||||
|
- Use a library like gin or echo
|
||||||
|
|
||||||
|
- Implement a daemon :
|
||||||
|
- Use a library like daemon or godo
|
||||||
|
|
||||||
|
- Implement a service :
|
||||||
|
- Use a library like consul or etcd
|
||||||
|
|
||||||
|
- Implement a cloud storage integration :
|
||||||
|
- Use a library like aws-sdk-go or google-cloud-go
|
||||||
|
|
||||||
|
- Implement a version control system integration :
|
||||||
|
- Use a library like git or mercurial
|
||||||
|
|
||||||
|
- Implement a backup system integration :
|
||||||
|
- Use a library like restic or borg
|
||||||
|
|
||||||
|
- Implement a file system watcher :
|
||||||
|
- Use a library like fsnotify or inotify
|
91
lab/TcpServExample.gone
Normal file
91
lab/TcpServExample.gone
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package lab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleMain() {
|
||||||
|
// Choose either server or client mode
|
||||||
|
if len(os.Args) > 1 && os.Args[1] == "server" {
|
||||||
|
runServer()
|
||||||
|
} else {
|
||||||
|
runClient()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runServer() {
|
||||||
|
listener, err := net.Listen("tcp", ":8080")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
fmt.Println("Server listening on :8080")
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error accepting connection:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConnection(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConnection(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
for {
|
||||||
|
message, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
fmt.Println("Client disconnected")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Error reading from client:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Received from client:", message)
|
||||||
|
|
||||||
|
// Process the message (e.g., send a response)
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Send a response to the client
|
||||||
|
_, err = conn.Write([]byte("Server received your message\n"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error writing to client:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runClient() {
|
||||||
|
conn, err := net.Dial("tcp", "localhost:8080")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
fmt.Println("Connected to server")
|
||||||
|
|
||||||
|
// Send a message to the server
|
||||||
|
_, err = conn.Write([]byte("Hello from the client!\n"))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error writing to server:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the server's response
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
message, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading from server:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Received from server:", message)
|
||||||
|
}
|
24
lab/algo.txt
Normal file
24
lab/algo.txt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
1. [On first setup] configure what files must be saved and their ID, configure sync mode you want to use (reflect up, reflect down, or bidirectionnal)
|
||||||
|
2. authenticate to the serv
|
||||||
|
3. [On first setup] Choose if you want to download or upload existing files
|
||||||
|
4. // index the files
|
||||||
|
5. // Check if changes were made, if not, do nothing
|
||||||
|
6. // Encode the files
|
||||||
|
7. Transfer the files over tcp with ssl
|
||||||
|
8. // Decode the files
|
||||||
|
9. // [On server] [if there are conflicts] take the last modified file
|
||||||
|
10. Reupdate every hour
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Authentication:
|
||||||
|
Register:
|
||||||
|
- client sends registration request to the server
|
||||||
|
- server returns fail/success code and a token
|
||||||
|
- client stores the token locally then authenticate
|
||||||
|
|
||||||
|
Authenticate:
|
||||||
|
- client sends token
|
||||||
|
- server returns bool isTokenValid and link client to the token temporarly
|
116
lab/core.gone
Normal file
116
lab/core.gone
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// core/sync.go
|
||||||
|
package lab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// File represents file metadata
|
||||||
|
type File struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
Size int64
|
||||||
|
Checksum string
|
||||||
|
ModTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync compares local and remote files and performs synchronization
|
||||||
|
func Sync(localRoot, remoteRoot string) error {
|
||||||
|
// 1. Scan local files
|
||||||
|
localFiles, err := scanFiles(localRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error scanning local files: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Scan remote files (replace with actual server communication)
|
||||||
|
remoteFiles, err := scanFiles(remoteRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error scanning remote files: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Compare and synchronize
|
||||||
|
for _, localFile := range localFiles {
|
||||||
|
// Check if file exists remotely
|
||||||
|
remoteFile, found := remoteFiles[localFile.Path]
|
||||||
|
if !found {
|
||||||
|
// Upload file
|
||||||
|
err := uploadFile(localFile.Path, remoteRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error uploading file: %w", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Compare timestamps
|
||||||
|
if localFile.ModTime.After(remoteFile.ModTime) {
|
||||||
|
// Upload updated file
|
||||||
|
err := uploadFile(localFile.Path, remoteRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error uploading file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Delete files that are no longer present locally
|
||||||
|
for _, remoteFile := range remoteFiles {
|
||||||
|
if _, found := localFiles[remoteFile.Path]; !found {
|
||||||
|
// Delete file remotely
|
||||||
|
err := deleteFile(remoteFile.Path, remoteRoot)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error deleting file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scanFiles recursively scans a directory and returns a map of file metadata
|
||||||
|
func scanFiles(root string) (map[string]*File, error) {
|
||||||
|
files := make(map[string]*File)
|
||||||
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
checksum := calculateChecksum(data)
|
||||||
|
files[path] = &File{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: path,
|
||||||
|
Size: info.Size(),
|
||||||
|
Checksum: checksum,
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return files, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// uploadFile uploads a file to the remote server
|
||||||
|
func uploadFile(localPath, remoteRoot string) error {
|
||||||
|
// Implement file upload logic
|
||||||
|
// ...
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteFile deletes a file from the remote server
|
||||||
|
func deleteFile(remotePath, remoteRoot string) error {
|
||||||
|
// Implement file deletion logic
|
||||||
|
// ...
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateChecksum calculates the checksum of a file
|
||||||
|
func calculateChecksum(data []byte) string {
|
||||||
|
// Implement checksum calculation logic
|
||||||
|
// ...
|
||||||
|
return ""
|
||||||
|
}
|
85
lab/main.gone
Normal file
85
lab/main.gone
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package lab
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
// "os"
|
||||||
|
"time"
|
||||||
|
// "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NotMain() {
|
||||||
|
|
||||||
|
TestServer()
|
||||||
|
// f, err := os.Open("/home/shared/Workplan/CoSync/lab/")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// files, err := f.Readdir(0)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for _, v := range files {
|
||||||
|
// println(v.Name(), v.IsDir())
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func Server() {
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", "localhost:4945")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
msg, err := io.ReadAll(conn)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.Write([]byte("hello from server"))
|
||||||
|
io.Closer.Close(conn)
|
||||||
|
|
||||||
|
print("[SERVER] ", conn.RemoteAddr().String(), " : ")
|
||||||
|
println(string(msg))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Client(message string) {
|
||||||
|
srv, err := net.Dial("tcp", "localhost:4945")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer srv.Close()
|
||||||
|
|
||||||
|
// format message
|
||||||
|
// msg := make([]byte, 0)
|
||||||
|
// msg = append(msg, []byte(message)...)
|
||||||
|
msg2 := []byte(message)
|
||||||
|
|
||||||
|
srv.Write(msg2)
|
||||||
|
println("[Client] Wrote msg")
|
||||||
|
|
||||||
|
//TODO: reprise
|
||||||
|
var b []byte
|
||||||
|
b2, err :=srv.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
println("[Client] Read msg : " + string(b2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServer() {
|
||||||
|
go Server()
|
||||||
|
go Client("this is a test")
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
println("[Main] finished")
|
||||||
|
}
|
172
lab/old client-main.gone
Normal file
172
lab/old client-main.gone
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
package lab
|
||||||
|
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os/user"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// "fmt"
|
||||||
|
// "io"q
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"cosync/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorRed = "\033[0;31m"
|
||||||
|
colorGreen = "\033[1;32m"
|
||||||
|
colorNone = "\033[0m"
|
||||||
|
separator = "\n ============================================== \n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
currentUser, _ = user.Current()
|
||||||
|
configFolder = "/home/" + currentUser.Username + "/.config/cosync/"
|
||||||
|
configFilePath = configFolder + "files.sync"
|
||||||
|
server = "https://sync.oblic-parallels.fr"
|
||||||
|
)
|
||||||
|
|
||||||
|
type file struct {
|
||||||
|
path string
|
||||||
|
modTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Print logo
|
||||||
|
println(" ______ ______ \n .' ___ | .' ____ \\ \n/ .' \\_| .--. | (___ \\_| _ __ _ .--. .---. \n| | / .'`\\ \\ _.____`. [ \\ [ ][ `.-. | / /'`\\] \n\\ `.___.'\\| \\__. || \\____) | \\ '/ / | | | | | \\__. \n `.____ .' '.__.' \\______.'[\\_: / [___||__]'.___.' \n \\__.' ")
|
||||||
|
|
||||||
|
// Open config file or create it if doesn't exists
|
||||||
|
configFile, err := os.Open(configFilePath)
|
||||||
|
if err == nil {
|
||||||
|
client.Setup()
|
||||||
|
}
|
||||||
|
defer configFile.Close()
|
||||||
|
|
||||||
|
// Get status from the server
|
||||||
|
resp, err := http.Get(server)
|
||||||
|
if err != nil {
|
||||||
|
print("\n\n" + colorRed)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Print server status
|
||||||
|
print(separator)
|
||||||
|
println("using server: ", server)
|
||||||
|
println("Response status:", resp.Status)
|
||||||
|
|
||||||
|
// Authenticate
|
||||||
|
// I'll do it later
|
||||||
|
|
||||||
|
print(separator)
|
||||||
|
println("Checking files :")
|
||||||
|
|
||||||
|
// Check if config file isn't empty
|
||||||
|
configFileStats, _ := configFile.Stat()
|
||||||
|
if configFileStats.Size() == 0 {
|
||||||
|
println("\n No files listed in config. There is nothing to do.")
|
||||||
|
println("\n\n > Exiting ...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and index all lines from config
|
||||||
|
|
||||||
|
configFileScanner := bufio.NewScanner(configFile)
|
||||||
|
register := make(map[string]file)
|
||||||
|
|
||||||
|
for configFileScanner.Scan() {
|
||||||
|
line := configFileScanner.Text()
|
||||||
|
|
||||||
|
print("\n ", line)
|
||||||
|
|
||||||
|
if line != "" {
|
||||||
|
index(line, register)
|
||||||
|
print(colorGreen + " ✔" + colorNone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println()
|
||||||
|
|
||||||
|
// Read from server
|
||||||
|
// scanner := bufio.NewScanner(resp.Body)
|
||||||
|
// for scanner.Scan() {
|
||||||
|
// println(scanner.Text())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err := scanner.Err(); err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
//test
|
||||||
|
// f, err := os.Open("/home/guillem/.vscode/extensions/extensions.json")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// defer f.Close()
|
||||||
|
|
||||||
|
// fs, _ := f.Stat()
|
||||||
|
// if fs.ModTime().Before()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func index(line string, register map[string]file) {
|
||||||
|
|
||||||
|
decomposedLine := strings.Split(line, " : ")
|
||||||
|
|
||||||
|
// check separator
|
||||||
|
if len(decomposedLine) != 2 {
|
||||||
|
println(colorRed + " ✘\n\nThis line is not valid" + colorNone)
|
||||||
|
panic(line + " is not a valid syntax")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if id is valid
|
||||||
|
if !IsValidID(decomposedLine[0]) {
|
||||||
|
println(colorRed + " ✘\n\nThis line is not valid" + colorNone)
|
||||||
|
panic("Id must be composed only by alphanumerical characters")
|
||||||
|
}
|
||||||
|
|
||||||
|
appendFiles(decomposedLine[0], decomposedLine[1], register)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsValidID(id string) bool {
|
||||||
|
return strings.ContainsFunc(id, IsSpecialRune)
|
||||||
|
}
|
||||||
|
func IsSpecialRune(r rune) bool {
|
||||||
|
return (r < 'A' || r > 'z' || (r > 'Z' && r < 'a')) && (r < '0' || r > '9') && r != '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendFiles(id string, path string, register map[string]file) {
|
||||||
|
|
||||||
|
// get subfiles and subfolders
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
println(colorRed + " ✘\n\n" + colorNone)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fstat, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
println(colorRed + " ✘\n\n" + colorNone)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if path points to a folder
|
||||||
|
if fstat.IsDir() {
|
||||||
|
files, err := f.ReadDir(0)
|
||||||
|
if err != nil {
|
||||||
|
println(colorRed + " ✘\n\n" + colorNone)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// call this function recursively for all subfiles and folders
|
||||||
|
for i := 0; i < len(files); i++ {
|
||||||
|
appendFiles(id+"-"+files[i].Name(), path+"/"+files[i].Name(), register)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
register[id] = file{path, fstat.ModTime()}
|
||||||
|
}
|
||||||
|
}
|
26
tests/tests.go
Normal file
26
tests/tests.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "testing"
|
||||||
|
"cosync/internal/client"
|
||||||
|
"cosync/internal/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAll() bool {
|
||||||
|
TestClientServerCommunication()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientServerCommunication() bool {
|
||||||
|
go client.Run()
|
||||||
|
go server.Run()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigLoading() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigCreation() bool {
|
||||||
|
return false
|
||||||
|
}
|
Loading…
Reference in a new issue