Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
phimage committed May 15, 2020
0 parents commit 3d7031d
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 0 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: release
on:
release:
types: [published]

jobs:
update:
name: build
runs-on: macOS-latest
steps:
- name: ⬇️ Checkout
uses: actions/checkout@master
with:
fetch-depth: 1
- name: 🏗 swiftbuild
run: |
swift build -c debug
- name: 📦 Build archive
run: |
REPOSITORY_NAME=$(jq --raw-output '.repository.name' $GITHUB_EVENT_PATH)
zip -r $REPOSITORY_NAME.zip .build/debug/$REPOSITORY_NAME
- name: ⬆️ Upload to Release
run: |
REPOSITORY_NAME=$(jq --raw-output '.repository.name' $GITHUB_EVENT_PATH)
ARTIFACT=./$REPOSITORY_NAME.zip
AUTH_HEADER="Authorization: token $GITHUB_TOKEN"
CONTENT_LENGTH_HEADER="Content-Length: $(stat -f%z "$ARTIFACT")"
CONTENT_TYPE_HEADER="Content-Type: application/zip"
RELEASE_ID=$(jq --raw-output '.release.id' $GITHUB_EVENT_PATH)
FILENAME=$(basename $ARTIFACT)
UPLOAD_URL="https://uploads.github.com/repos/$GITHUB_REPOSITORY/releases/$RELEASE_ID/assets?name=$FILENAME"
echo "$UPLOAD_URL"
curl -sSL -XPOST \
-H "$AUTH_HEADER" -H "$CONTENT_LENGTH_HEADER" -H "$CONTENT_TYPE_HEADER" \
--upload-file "$ARTIFACT" "$UPLOAD_URL"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 changes: 17 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Swift

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: macos-latest

steps:
- uses: actions/checkout@v2
- name: Build
run: swift build -v
23 changes: 23 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
included:
- Sources

# rule identifiers to exclude from running
disabled_rules:
- cyclomatic_complexity
- large_tuple
- todo

shorthand_operator: warning

# configurable rules can be customized from this configuration file
line_length:
- 200
- 250

function_body_length:
- 50
- 100

file_length:
- 500
- 600
25 changes: 25 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "cd2sql",
products: [
.executable(
name: "cd2sql", targets: ["cd2sql"]
)
],
dependencies: [
.package(url: "https://github.com/phimage/MomXML" , .upToNextMajor(from: "1.1.0")),
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.6"),
.package(url: "https://github.com/nvzqz/FileKit.git", from: "6.0.0")
],
targets: [
.target(
name: "cd2sql",
dependencies: ["MomXML", "ArgumentParser", "FileKit"],
path: "Sources"
)
]
)
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# CoreData to SQL

![Swift](https://github.com/phimage/cd2sql/workflows/Swift/badge.svg)
![release](https://github.com/phimage/cd2sql/workflows/release/badge.svg)

Convert CoreData model to SQL

## Install

### Using release

Go to https://github.com/phimage/cd2sql/releases and take the last binary for macOS cd2sql.zip

### Using sources

```
git clone https://github.com/phimage/cd2sql.git
cd cd2sql
swift build -c release
```

Binary result in `.build/release/cd2sql`

## Usage

```
cd2sql <core data model>
```

### example

```
cd2sql /path/to/MyModel.xcdatamodeld
```

```sql
CREATE TABLE Personne (
FirstName VARCHAR,
LastName VARCHAR,
ID INTEGER PRIMARY KEY
);
```
94 changes: 94 additions & 0 deletions Sources/Command.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// Command.swift
//
// Created by phimage on 15/05/2020.
//

import Foundation
import ArgumentParser
import MomXML
import SWXMLHash
import FileKit

struct Command: ParsableCommand {

static let configuration = CommandConfiguration(abstract: "Transform core data model to SQL")

@Option(help: "The core data model path.")
var path: String?

@Argument(help: "The core data model path.")
var pathArg: String?

@Option(default: "", help: "Format sush as 'sqlite'")
var format: String? // SQLite

@Option(default: "keyMapping", help: "Search table and field names inside userinfo using this key")
var mapping: String?

@Option(help: "userinfo key used to find primary key (default: primaryKey)")
var primaryKey: String?

// @Flag [IF NOT EXISTS]

func validate() throws {
guard let path = self.path ?? self.pathArg else {
throw ValidationError("'<path>' of core data model not specified.")
}
guard Path(path).exists else {
throw ValidationError("'<path>' \(path) doesn't not exist.")
}
}

func run() throws {
var modelURL = URL(fileURLWithPath: self.path ?? self.pathArg ?? "")
if modelURL.pathExtension == "xcdatamodeld" {
modelURL = modelURL.appendingPathComponent("\(modelURL.deletingPathExtension().lastPathComponent).xcdatamodel")
}
if modelURL.pathExtension == "xcdatamodel" {
modelURL = modelURL.appendingPathComponent("contents")
}

let xmlString = try String(contentsOf: modelURL)
let xml = SWXMLHash.parse(xmlString)
guard let parsedMom = MomXML(xml: xml) else {
error("Failed to parse \(modelURL)")
return
}
let sqliteLite = format == "sqlite"

for entity in parsedMom.model.entities {
let tableName = entity.name(sqlite: sqliteLite, mapping: mapping)
var sql = "CREATE TABLE \(tableName) (\n"
let primaryKey = entity.userInfo[self.primaryKey ?? "primaryKey"]

var first = true
for attribute in entity.attributes {
if first {
first = false
} else {
sql += ",\n"
}
let attributeName = attribute.name(sqlite: sqliteLite, mapping: mapping)
sql += " \(attributeName) \(attribute.attributeType.sqliteName)"
if !attribute.isOptional {
sql += " NOT NULL"
}
if attributeName == primaryKey {
sql += " PRIMARY KEY"
}
}
sql += "\n);\n"
log(sql)
}
}

func log(_ message: String) {
print(message)
}

func error(_ message: String) {
print("❌ error: \(message)") // TODO: output in stderr
}

}
67 changes: 67 additions & 0 deletions Sources/Helpers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Foundation
import MomXML

protocol MomType {
var name: String {get}
var sqliteName: String {get}
var userInfo: MomUserInfo {get}
}

extension MomEntity: MomType {

var sqliteName: String {
return "Z\(name.uppercased())"
}
}
extension MomAttribute: MomType {

var sqliteName: String {
return "Z\(name.uppercased())"
}
}

extension MomType {
func name(sqlite: Bool, mapping: String?) -> String {
if sqlite {
return self.sqliteName
} else {
if let mapping = mapping, let name = self.userInfo[mapping] {
return name
}
return self.name
}
}
}

extension MomAttribute.AttributeType {

var sqliteName: String {
switch self {
case .string:
return "VARCHAR"
case .date:
return "TIMESTAMP"
case .integer32:
return "INTEGER"
case .boolean:
return "INTEGER"
case .binary, .transformable:
return "BLOB"
default:
return self.rawValue.uppercased()
}
}

}

extension MomUserInfo {

subscript(key: String) -> String? {
for userInfo in self.entries {
if userInfo.key == key {
return userInfo.value
}
}
return nil
}
}
2 changes: 2 additions & 0 deletions Sources/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Run command
Command.main()

0 comments on commit 3d7031d

Please sign in to comment.