Last active
March 17, 2017 23:18
-
-
Save aspyct/e21878cf5e40fe6589c7847f09d6700e to your computer and use it in GitHub Desktop.
Generate a .immutable.cs containing constructors and Derive() method for an immutable c# class.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Usage: ./immutable.py path/to/State.cs | |
# Will generate/overwrite State.immutable.cs | |
import sys | |
import re | |
import os.path | |
def regex(pattern): | |
return re.compile(pattern, re.I | re.M) | |
using_pattern = regex(r'^using\s+([^;]+)\s*;\s*$') | |
class_pattern = regex(r'^\s+partial class\s+([^\s]+)\s*$') | |
namespace_pattern = regex(r'^namespace\s+([^\s]+)') | |
property_pattern = regex(r'^\s*public\s+readonly\s+(.+?)\s+([\w_]+)\s+=') | |
generated_warning = "// File generated by immutable.py" | |
input_path = os.path.realpath(sys.argv[1]) | |
basename, ext = os.path.splitext(input_path) | |
output_path = "{}.immutable{}".format(basename, ext) | |
# Verify that we can overwrite this file | |
if os.path.isfile(output_path): | |
with open(output_path) as f: | |
first_line = f.readline().decode('utf-8-sig').strip() | |
if first_line != generated_warning: | |
# It's not our file, abort | |
print("This doesn't seem to be a generated file:") | |
print(output_path) | |
print("I won't overwrite it. Aborting...") | |
sys.exit(1) | |
with open(input_path) as cs: | |
source = cs.read().decode('utf-8-sig') | |
usings = using_pattern.findall(source) | |
namespace = namespace_pattern.findall(source) | |
cls = class_pattern.findall(source) | |
properties = property_pattern.findall(source) | |
assert len(namespace) == 1 | |
assert len(cls) == 1 | |
indent = " " | |
template_params = { | |
'namespace': namespace[0], | |
'class': cls[0], | |
'usings': "\n".join( | |
map(lambda x: "using %s;" % x, usings) | |
), | |
'ctor_params': (",\n" + indent * 3).join( | |
map(lambda x: " ".join(x), properties) | |
), | |
'ctor_statements': ("\n" + indent * 3).join( | |
map(lambda (_, p): "this.%s = %s;" % (p, p), properties) | |
), | |
'derive_params': (",\n" + indent * 3).join( | |
map(lambda x: " ".join(x) + " = null", properties) | |
), | |
'derive_statements': (",\n" + indent * 4).join( | |
map(lambda (_, p): "%s: %s ?? this.%s" % (p, p, p), properties) | |
), | |
'generated_warning': generated_warning | |
} | |
template = """{generated_warning} | |
// https://gist.github.com/aspyct/e21878cf5e40fe6589c7847f09d6700e | |
{usings} | |
namespace {namespace} | |
{{ | |
partial class {class} | |
{{ | |
public {class}() {{}} | |
private {class}( | |
{ctor_params} | |
) | |
{{ | |
{ctor_statements} | |
}} | |
public {class} Derive( | |
{derive_params} | |
) | |
{{ | |
return new {class}( | |
{derive_statements} | |
); | |
}} | |
}} | |
}}""" | |
with open(output_path, 'w') as outf: | |
generated = template.format(**template_params) | |
outf.write(generated) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Monad; | |
namespace NightOut.Core.ViewModel.CheckIn.LocationSelector | |
{ | |
[Serializable] | |
partial class State | |
{ | |
public readonly Maybe<string> longitude = Nothing<string>.Default; | |
public readonly Maybe<string> latitude = Nothing<string>.Default; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// File generated by immutable.py | |
// https://gist.github.com/aspyct/e21878cf5e40fe6589c7847f09d6700e | |
using System; | |
using Monad; | |
namespace NightOut.Core.ViewModel.CheckIn.LocationSelector | |
{ | |
partial class State | |
{ | |
public State() {} | |
private State( | |
Maybe<string> longitude, | |
Maybe<string> latitude | |
) | |
{ | |
this.longitude = longitude; | |
this.latitude = latitude; | |
} | |
public State Derive( | |
Maybe<string> longitude = null, | |
Maybe<string> latitude = null | |
) | |
{ | |
return new State( | |
longitude: longitude ?? this.longitude, | |
latitude: latitude ?? this.latitude | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment