(warning: this was posted also on https://forums.fsharp.org/t/asyncresult-and-handling-rollback/928)
Trying to implement 2PC Transaction Like workflow in F# (see http://learnmongodbthehardway.com/article/transactions/) and hitting an issue with computation expressions (asyncResult for example) and rollback.
If you have the following pseudocode:
let rollbackWorkflow parX parY =
… here calling rollbackService1 and rollbackService2
let executeWorkflow par1 par2 par3 =
asyncResult {
let! result1 = callService1 x y z
let! result2 = callService2 x2 y2 z2
}
how can I check in executeWorkflow if result1 and/or result2 is Error and then call rollbackWorkflow function?? Should I change callService1 and callService2 to raise exceptions instead of returning results also in expected error cases (not sufficient funds, limits exceeded), or should I use some function like teeError? Any suggestion highly appreciated!
P.S. This is what I want to eventually implement:
function executeTransaction(from, to, amount) {
var transactionId = ObjectId();
transactions.insert({
_id: transactionId,
source: from,
destination: to,
amount: amount,
state: “initial”
});
var result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “pending” } }
);
if (result.modifiedCount == 0) {
cancel(transactionId);
throw Error(“Failed to move transaction " + transactionId + " to pending”);
}
// Set up pending debit
result = accounts.updateOne({
name: from,
pendingTransactions: { $ne: transactionId },
balance: { $gte: amount }
}, {
$inc: { balance: -amount },
$push: { pendingTransactions: transactionId }
});
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to debit " + from + " account”);
}
// Setup pending credit
result = accounts.updateOne({
name: to,
pendingTransactions: { $ne: transactionId }
}, {
$inc: { balance: amount },
$push: { pendingTransactions: transactionId }
});
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to credit " + to + " account”);
}
// Update transaction to committed
result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “committed” } }
);
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to move transaction " + transactionId + " to committed”);
}
// Attempt cleanup
cleanup(from, to, transactionId);
}
executeTransaction(“Joe Moneylender”, “Peter Bum”, 100);
Just do some error handling outside the workflow, like this:
type TransactionError =
| NoFunds
| Other
let rollbackWorkflow parX parY = async.Return ( printfn "here calling rollbackService1 and rollbackService2"; Ok -1 )
let callService1 parX parY = async.Return ( printfn "callService1"; if parX + parY > 0 then Ok 1 else Error NoFunds )
let callService2 parX parY = async.Return ( printfn "callService2"; if parX + parY > 0 then Ok 2 else Error Other )
let executeWorkflow par1 par2 par3 =
asyncResult {
let! result1 = callService1 par1 par2
let! result2 = callService2 result1 par3
return result2
} |> AsyncResult.bindError (fun x -> if x = NoFunds then rollbackWorkflow 0 1 else rollbackWorkflow 1 0)
I wrote that example with the AsyncResult from the code you linked. Plus bindError which should be something like:
/// Apply a monadic function to an AsyncResult error
let bindError (f: 'a -> AsyncResult<'b,'c>) (xAsyncResult : AsyncResult<_, _>) :AsyncResult<_,_> = async {
let! xResult = xAsyncResult
match xResult with
| Ok x -> return Ok x
| Error err -> return! f err
}
If you think about it, bindError is like a pure version of the catch function, see for example this code fragment , using another library.
Related
I want to parse the string
"{\"hello , world\",quote,\"\\\",\\\"q\"}"into vec!["hello , world", "quote", "\",\"q"]
I have tried to for loop and check " then split , but it has many corner cases that I couldn't solve, such as \" in the quote.
I got it! Just for loop and check the tokens. Then, use serde_json to parse the data.
fn parse_code(code: &str) -> Vec<String> {
let code = code.trim_end_matches("}").trim_start_matches("{");
let mut ans = Vec::new();
let mut start_string = false;
let mut extra = false;
let mut pre = '\0';
for ch in code.chars() {
if ch == '"' && pre != '\\' {
start_string = !start_string;
}
if pre == ',' && start_string == false {
start_string = true;
extra = true;
ans.push('"');
}
if ch == ',' && start_string == true && extra == true {
start_string = false;
extra = false;
ans.push('"');
}
ans.push(ch);
pre = ch;
}
if extra {
ans.push('"');
}
let ans: String = ans.iter().collect();
let code = "[".to_string() + &ans + "]";
serde_json::from_str::<Value>(&code)
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|x| x.as_str().unwrap().to_string())
.collect()
}
I'm using autocode for this question, Every time I rerun the command there is always an error that said "Cannot Read Property 'split' of undefined" Here is the code below:
const event = context.params.event
const { guild_id, token, channel_id, member, author} = event;
const error = require('../../../helpers/error.js')
try {
if (!event?.mentions?.length) throw new Error(`You need to mention a user!`);
let emojis = await lib.utils.kv['#0.1.16'].get({key: `emojis`, defaultValue: {moderator: null, user: null, reason: null, channel: null, error: null, timeout: null, clock: null}});
let base = await lib.airtable.query['#1.0.0'].select({table: `Config`, where: [{'Guild__is': guild_id}]});
if (!base?.rows?.length) base = await lib.airtable.query['#1.0.0'].insert({table: `Config`, fieldsets: [{'Guild': guild_id}]});
let cNum = (await lib.airtable.query['#1.0.0'].max({table: `Cases`, where: [{'Guild__is': guild_id}], field: `Case`})).max.max + 1
let isAdmin = false, targetAdmin = false, reason = event.content.split(' ').slice(3).join(' ') || `No reason provided`
let guildInfo = await lib.discord.guilds['#0.2.4'].retrieve({guild_id});
let target = await lib.discord.guilds['#0.2.4'].members.retrieve({user_id: event.mentions[0].id, guild_id});
if (guildInfo.owner_id == author.id) isAdmin = true
if (guildInfo.owner_id == target.user.id) targetAdmin = true
if (member.roles.includes(base.rows[0].fields.Moderator)) isAdmin = true
if (target.roles.includes(base.rows[0].fields.Moderator)) targetAdmin = true
if (guildInfo.roles.filter(role => (role.permissions & (1 << 3)) === 1 << 3 && member.roles.includes(role.id))?.length) isAdmin = true
if (guildInfo.roles.filter(role => (role.permissions & (1 << 3)) === 1 << 3 && target.roles.includes(role.id))?.length) targetAdmin = true
if (!isAdmin)
return lib.discord.channels['#release'].messages.create({channel_id, content: `<:error:${emojis.error}> You don't have permission to use \`sm.mute\``});
if (targetAdmin)
return lib.discord.channels['#release'].messages.create({channel_id, content: `<:error:${emojis.error}> You can't mute a member who is mod/admin`});
let match = event.context.split(' ')[2].match(/(\d+)(s|m|h|d)/)
if (!match)
throw new Error(`Please provide a duration in days(\`d\`), hours(\`h\`), minutes(\`m\`) or seconds(\`s\`).`)
let secs = new Date().getSeconds()
let time = parseInt(match[1]), unit = match[2]
if (unit === 's') time *= 1
else if (unit === 'm') time *= 60
else if (unit === 'h') time *= 60 * 60
else if (unit === 'd') time *= 24 * 60 * 60
let until = `<t:${Math.floor(new Date().setSeconds(secs + time) / 1000)}>`
await lib.discord.guilds['#release'].members.timeout.update({user_id: target.user.id, guild_id, communication_disabled_until_seconds: time, reason});
await lib.discord.channels['#release'].messages.create({channel_id, content: `<:succes:${emojis.success}>**${target.user.username}** was muted for ${match[1]} ${match[2].replace('s', 'seconds').replace('d', 'days').replace('h', 'hours').replace('m', 'minutes')} | ${reason}`});
if (base.rows[0].fields.Logging) {
await lib.discord.channels['#release'].messages.create({
channel_id: base.rows[0].fields.Logging,
content: ``,
embeds: [{
type: 'rich',
color: 0xE72020,
description: `
<:user:${emojis.user}> | <#${target.user.id}> | ${target.user.username}#${target.user.discriminator}
<:moderator:${emojis.moderator}> | <#${author.id}> | ${author.username}#${author.discriminator}
<:reason:${emojis.reason}> | ${reason}
<:timeout:${emojis.timeout} | ${until.replace('>', ':R>')} | ${time}s`,
footer: {text: `Case Number: ${cNum}`},
author: {name: `${cNum} | Mute`, icon_url: target.user.avatar_url || 'https://cdn.discordapp.com/attachments/911294620439293993/965586325208195142/avatar.png'},
timestamp: new Date().toISOString(),
}],
});
}
await lib.airtable.query['#1.0.0'].insert({table: `Cases`,
fieldsets: [{
User: target.user.id,
Reason: reason,
Type: `Mute`,
Moderator: author.id,
Timestamp: `<t:${Math.floor(new Date().getTime() / 1000)}>`,
Case: cNum,
Guild: guild_id,
}],
});
} catch (e) {
await error.prefix(channel_id, e.message)
Help me! I need some help to fix this. Please explain this answer of how to fix "Cannot Read Property 'split' of undefined"
Error reported always until I added a manual check point to save the data frame after cube and before pivot.
Only manual save and reload the table works, other ways such as persist or check point all no use.
My question is how to avoid this. The error message:
Conflicting attributes: groupby_keys#90179
;;
'Join UsingJoin(Inner,List(groupby_keys))
My code is a little mess for to test in spark-shell but anyways:
case class FlagsStr(groupby_keys: String, profit_flag_str: String, cmd_str: String, open_followed_str: String, close_followed_str: String, close_or_open_flag_str: String, point: Int)
import org.apache.spark.sql.functions._
import org.apache.spark.sql.SparkSession
import org.apache.spark.storage.StorageLevel
def merge_flags(flag1: String, flag2: String): String = {
var ret: String = null
if( flag1 != null & flag2 != null)
ret = flag1 + "_" + flag2
else if ( flag1 != null || flag2 != null) {
if( flag1 != null)
ret = flag1
else
ret = flag2
} else ret = null
ret
}
def concat_str_flags = udf((profit_flag_str: String, cmd_str: String, open_followed_str: String, close_followed_str: String, close_or_open_flag_str: String) => {
//val retArr: Array[String] = Array(str_flags:_*)
val retArr: Array[String] = Array(profit_flag_str, cmd_str, open_followed_str, close_followed_str, close_or_open_flag_str)
val ret = retArr.reduce(merge_flags)
ret
})
def add_target_2_flags(flag: String, prefix: String): String = {
if( flag != null)
prefix + "_" + flag
else
prefix
}
def add_target_agg_2_flags(flag: String, prefix: String, suffix: String): String = {
if( flag != null)
prefix + "_" + flag + "_" + suffix
else
prefix + "_" + suffix
}
def add_order_2_flags = udf((flag: String) => {
add_target_2_flags(flag, "order")
})
def add_point_2_flags = udf((flag: String) => {
add_target_2_flags(flag, "point")
})
def add_point_max_2_flags = udf((flag: String) => {
add_target_agg_2_flags(flag, "point", "max")
})
val ss = SparkSession.builder().appName("TestCubePivot").enableHiveSupport().getOrCreate()
import ss.implicits._
val sqlContext = ss.sqlContext
val flagsStrDataFrame = sqlContext.createDataFrame(List(
FlagsStr("3_223", "profit", "long", "os", "cs", "close", 5),
FlagsStr("4_423", null, "long", "os", "cs", "close", 10),
FlagsStr("5_523", "profit", null, "os", "cs", "close", 15)
))
val h_mt4trades_with_target_agg_4_pivot_in_memory=flagsStrDataFrame .cube($"groupby_keys", $"profit_flag_str", $"cmd_str", $"open_followed_str", $"close_followed_str", $"close_or_open_flag_str") .agg(
count($"*").as("count_1"),
sum($"point").as("sum_point"),
max($"point").as("max_point")
) .filter($"groupby_keys".isNotNull) .withColumn("flags",concat_str_flags($"profit_flag_str", $"cmd_str", $"open_followed_str", $"close_followed_str", $"close_or_open_flag_str"))
h_mt4trades_with_target_agg_4_pivot_in_memory.write.format("orc").saveAsTable("h_mt4trades_with_target_agg_4_pivot_in_memory")
val h_mt4trades_with_target_agg_4_pivot = sqlContext.read.table("h_mt4trades_with_target_agg_4_pivot_in_memory")
//h_mt4trades_with_target_agg_4_pivot.checkpoint
h_mt4trades_with_target_agg_4_pivot.persist(StorageLevel.MEMORY_AND_DISK)
ss.sparkContext.broadcast(h_mt4trades_with_target_agg_4_pivot)
val h_mt4trades_stat_cube_order = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_order", add_order_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_order") .max("count_1")
//h_mt4trades_stat_cube_order.checkpoint
val h_mt4trades_stat_cube_point = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_point", add_point_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_point") .max("sum_point")
//h_mt4trades_stat_cube_point.checkpoint
val h_mt4trades_stat_cube_point_max = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_point_max", add_point_max_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_point_max") .max("max_point")
val test = h_mt4trades_stat_cube_order.join(h_mt4trades_stat_cube_point, "groupby_keys").join(h_mt4trades_stat_cube_point_max, "groupby_keys")
The background to the question is, that I would like to create software change requests from Doors requirement changes.
For this I have to get the differences of requirements between two user selectable baselines of a module in a human readable format.
In the GUI I use the "Baseline Compare" function.
How can I access these results from a script (inside or outside of Doors) in a structured format?
A good starting point might the Doors DXL library Tools -> DXL Library -> baseline comparator example, but it needs heavy modifications for your overall use-case I guess. Take a look.
Found a modified version of the baseline compare script at this forum . I haven't tried it yet, but here's the code, which does use a GUI but you should be able to redirect the input & output pretty easily:
// Your Company Baseline Compare with Attributes
/*
Date Posted: 16-Jan-2008 00:03
Posted By: Juergen Albrecht
*/
/*
// (Your Company Baseline Compare) *
//---------------------------------------------------------------------------*
// *
// Project: Your Company Ottobrunn *
// *
//---------------------------------------------------------------------------*
// Module: all modules for baseline compare *
//---------------------------------------------------------------------------*
// Description: ... *
// *
// Functions: compare any two baselines of the current module *
// (or a baseline against the current version). *
// Sets a filter on objects which differ with request *
// to Object Heading, Object Text, Object Type and .... *
// *
// *
// Copyright (C) Your Company Your Town 2005-2007 Confidential. All rights reserved. *
//---------------------------------------------------------------------------*
// *
// $Header: /doors/lib/dxl/addins/user/BaselineCompare.dxl Ver. 1.0 15.03.06 18:00 UID-USER $
// *
//---------------------------------------------------------------------------*
// $History: BaselineCompare.dxl $
//
//********************************* Version 1.0 ****************************
// User: Date: Time: *
// Jürgen Albrecht (uid-al28423) 15.01.08 18:00
// Created in $/doors/lib/dxl/addins/user/BaselineCompare.dxl
//---------------------------------------------------------------------------*
// *
// *
// *
//---------------------------------------------------------------------------*
*/
pragma runLim, 0
////////////////////////////////////////////////////// constants for Attributes
const string DUMMY_LIST[] = {}
const string strAffectsArr[] = {"Ch.Bars", "Ch.Date", "History"}
const string strCharacterArr[] = {"System ", "Hidden ", "Customised"}
const string strValidityArr[] = {"Module ", "Object"}
const string strPropertyArr[] = {"Inherited", "Multi value"}
////////////////////////////////////////////////////// variables for Attributes
DB dbBLCompare = null
DBE dbeAttrDefNameList = null
DBE dbeAttrCharacter = null
DBE dbeAttrAffects = null
DBE dbeAttrValidity = null
DBE dbeAttrProperty = null
DBE dbeAttrType = null
DBE dbeDefaultValue = null
DBE dbeAttrTypeList = null
DBE dbeAttrTypeSelect = null
////////////////////////////////////////////////////// functions for Attributes
int chopInsert2(DBE inlist, string s)
{ int no_inlist = noElems inlist
int low = 0
int high = no_inlist-1
int hw = 0
string c
if (no_inlist == 0)
{ insert(inlist,0,s)
setCheck(inlist,0,false)
return 0
}
while (high-low > 1)
{ hw = low + ((high-low) / 2)
c = get(inlist,hw)
if (s < c)
high = hw
else
low = hw
}
c = get(inlist,high)
if (c < s)
{ insert(inlist,high+1,s)
setCheck(inlist,high+1,false)
return high + 1
}
c = get(inlist,low)
if (c > s)
{ insert(inlist,low,s)
setCheck(inlist,low,false)
return low
}
insert(inlist,low+1,s)
setCheck(inlist,low+1,false)
return low+1
} // chopInsert2
void populateLstAttrDef(bool bIncludeSystemAttr)
{ AttrDef ad
for ad in current Module do
{ if (ad.module)
continue
if (!bIncludeSystemAttr)
{ if (ad.system || ad.hidden)
continue
}
chopInsert2(dbeAttrDefNameList, ad.name "")
}
set(dbeAttrDefNameList, 0, true)
} // populateLstAttrDef
void doNothing(DBE dbeKlick, int iKlick)
{
} // doNothing
void cbSystemSelect(DBE dbeKlick)
{ bool bSelect = get(dbeAttrTypeSelect)
empty (dbeAttrDefNameList)
populateLstAttrDef (bSelect)
} // cbSystemSelect
void doSetAttrTypes(void)
{
AttrDef ad = find(current Module, get(dbeAttrDefNameList, get(dbeAttrDefNameList)))
AttrType at = find(current Module, ad.typeName "")
string strDefVal = "", strAttrType = "", s = ""
int iAttrChar = 0, iAttrValid = 0, iProperty = 0, iAffects = 0
if (ad.system)
iAttrChar = 1
else
iAttrChar = 4
if (ad.hidden)
iAttrChar = iAttrChar | 2
set(dbeAttrCharacter, iAttrChar)
if (ad.object)
iAttrValid = 2
if (ad.module)
iAttrValid = iAttrValid | 1
set(dbeAttrValidity, iAttrValid)
if (ad.inherit)
iProperty = 1
if (ad.multi)
iProperty = iProperty | 2
set(dbeAttrProperty, iProperty)
if (!ad.nobars)
iAffects = 1
if (!ad.nochanges)
iAffects = iAffects | 2
if (!ad.nohistory)
iAffects = iAffects | 4
set(dbeAttrAffects, iAffects)
if ( null at )
{ strAttrType = "Unable to determine type of attribute"
set(dbeAttrType, strAttrType)
return
}
strAttrType = at.type ""
if ( at.type == attrInteger )
{ int i
if ( at.minValue )
{ i = at.minValue
s = s "Min > " i "\n"
}
if ( at.maxValue )
{ i = at.maxValue
s = s "Max < " i "\n"
}
}
else if ( at.type == attrReal )
{ real r
if ( at.minValue )
{ r = at.minValue
s = s "Min > " r "\n"
}
if ( at.maxValue )
{ r = at.maxValue
s = s "Max < " r "\n"
}
}
else if ( at.type == attrDate )
{ Date d
if ( at.minValue )
{ d = at.minValue
s = s "Min > " d "\n"
}
if ( at.maxValue )
{ d = at.maxValue
s = s "Max < " d "\n"
}
}
else if ( at.type == attrEnumeration )
{ string strAttrTypeName = at.name
strAttrType = strAttrType ": " strAttrTypeName
int i
for ( i = 0; i < at.size; ++i )
s = s at.strings[i] "\n"
}
set(dbeAttrType, strAttrType)
set(dbeAttrTypeList, s)
if (ad.defval)
{ if (strDefVal == "")
strDefVal = ad.defval
set (dbeDefaultValue, strDefVal)
}
else
set (dbeDefaultValue, "")
// Ausgabe in File
// output << ad.name"\n"
// output << s"\n"
} // doSetAttrTypes
void cbDoSetAttrTypes(DBE dbeKlick, int iKlick)
{ doSetAttrTypes
} // cbDoSetAttrTypes
void showAttributes()
{ if ( null current Module )
{ ack "This tool must be run within a module..."
halt
}
// Ausgabe in File
// output << "\t\t"(current Module)."Name""\n"
populateLstAttrDef(true)
doSetAttrTypes()
// close output
} // showAttrInvestigatorDB
/////////////////////////////////////////// Definition for Modules
DBE lblOldList, lblNewList
DBE dbeListCurrent, dbeListBaseline // two global lists containing the baseline selected (or current version)
DBE dbeOutFileBrowse, dbeOutFileLabel
DBE dbeChangeBarToggle
DBE dbeShowDiffToggle
Stream outFile
Skip skBaselines = create // cache current baselines
bool bShowDiff
string strCellBorders = "\\clbrdrt\\brdrs\\brdrw15\\brdrcf11 \\clbrdrl\\brdrs\\brdrw15\\brdrcf11 \\clbrdrb\\brdrs\\brdrw15\\brdrcf11 \\clbrdrr\\brdrs\\brdrw15\\brdrcf11 "
string lastHeadingNumber(Object o)
{ Regexp reg = regexp "\\.0-[0-9]+$"
string onum = number o
if (onum[0:0] == "0")
{ return "0"
}
else if (reg onum)
{ int i = start 0
string hd = onum[0:i-1]
while (reg hd)
{ int j = start 0
hd = hd[0:j-1]
}
return hd
}
else
{ return onum
}
} // lastHeadingNumber
bool oleInsDel(Object o1,o2)
{ bool oleRec = false
string modType = ""
int oldOle = oleCount(o2."Object Text")
int newOle = oleCount(o1."Object Text")
if (newOle > oldOle)
{ oleRec = true
modType = "Figure/Table inserted"
}
if (newOle < oldOle)
{ oleRec = true
modType = "Figure/Table deleted"
}
if (oleRec)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl " identifier(o1) " section " lastHeadingNumber(o1) "\\cell OLE\\cell " modType "\\cell \\cell\\row\n"
}
return oleRec
} // oleInsDel
bool oleChange(Object o1,o2)
{ int i = oleCount o1."Object Text"
if (i > 0)
{ Buffer b1 = create
Buffer b2 = create
b1 = richTextWithOle o1."Object Text"
b2 = richTextWithOle o2."Object Text"
int l1 = length b1
int l2 = length b2
delete b1
delete b2
if (l1 != l2)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl " identifier(o1) " section " lastHeadingNumber(o1) "\\cell OLE\\cell Figure/Table modified\\cell \\cell\\row\n"
accept o1 // set filter
accept o2 // on both objects
return true
}
}
return false
} // oleChange
bool compareObjects(int absno, Object o1, o2, string attr)
{ // function to compare an attribute of two objects with same absolute number
Buffer s1 = create
Buffer s2 = create
Buffer s3 = create
s1 = o1.attr
s2 = o2.attr
if (s1!=s2)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
// if ((attr=="Object Text") && bShowDiff)
if (bShowDiff)
// {
// diff(s3, s2, s1,"\\cf1\\strike ","\\cf3\\ul ")
// outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx9531\n"
// outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " s3 "\\cell " s2 "\\cell\\row\n"
// }
{ diff(s3, s2, s1)
Regexp ct = regexp "colortbl[^}]*}"
string frag
if (ct s3)
{ frag = s3[end 0 + 1:(length s3) - 3]
}
else
{ frag = richTextFragment stringOf(s3)
}
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " frag "\\cell\\row\n"
}
else
{ outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " s1 "\\cell " s2 "\\cell\\row\n"
}
accept o1 // set filter
accept o2 // on both objects
delete s1
delete s2
delete s3
return false
}
delete s1
delete s2
delete s3
return true
} // compareObjects
Skip getAbsnos(Module m)
{ // Build a skip list which maps absnos onto their corresponding objects. Also initialize the DXL filter to "reject"
Skip res = create
Object o
for o in m do
{ int a = o."Absolute Number"
reject o // filter those mentioned in report
put(res, a, o)
}
return res
} // getAbsnos
void applyCompareFunction (DB dbKlick)
{ // Main comparison routine: find out which modules to compare
// compare objects present in both, report on
// those present in only one.
string name1, name2, outf
int idx1, idx2
bool updateChangeBars
idx1 = get dbeListCurrent // position in list
idx2 = get dbeListBaseline
name1 = get dbeListCurrent // baseline name
name2 = get dbeListBaseline
outf = get dbeOutFileBrowse
updateChangeBars = get dbeChangeBarToggle
bShowDiff = get dbeShowDiffToggle
if (idx1 < 0 || idx2 < 0) // error checking
{ ack "two selections are needed"
return
}
else if (idx1 == idx2)
{ ack "same selection on both sides"
return
}
Regexp slash = regexp "[\\\\]"
if (!slash outf)
{ outf = currentDirectory "\\" outf
}
Baseline sel1, sel2
outFile = write outf
outFile << "{\\rtf1\\deff0{\\fonttbl{\\f0\\fswiss\\fcharset177 Times New Roman;}}{\\colortbl ;\\red255\\green0\\blue0;\\red0\\green255\\blue0;\\red0\\green0\\blue255;}\n"
outFile << "\\paperw11906\\paperh16838\\margl1134\\margr567\\margt1134\\margb851\\headery567\\footery567\n"
string where = (current Module)."Name"
outFile << "\\ul \\fs24 Modified Objects in Module: " where "\\ul0 \\par \\par\n"
outFile << "\\trowd \\trgaph108\\trleft-108\\trhdr"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs20 Identifier\\cell Attribute\\cell Current Baseline\\cell Old Baseline\\cell\\row\n"
string str
for str in skBaselines do // find each baseline
{ Baseline b = key skBaselines // the baseline is the key
string str = (major b) "." (minor b) (suffix b)
if (name1==str) sel1 = b
if (name2==str) sel2 = b
}
progressStart(dbBLCompare, "Baseline Compare", "", 1)
Module old = current
Module b1, b2
if (idx1==0)
b1 = old // i.e. the current Module
else
{ progressMessage("Load First Baseline Module")
b1 = load(sel1, true) // load the baselines on the screen
}
progressStop()
progressStart(dbBLCompare, "Baseline Compare", "", 1)
if (idx2==0)
b2 = old // i.e.e the current Module
else
{ progressMessage("Load Second Baseline Module")
b2 = load(sel2, true)
}
progressStop()
if (updateChangeBars and (idx1 == 0))
{ AttrDef atcb
atcb = find(b1,"ChangeBar")
if (null atcb)
{ updateChangeBars = false
ack "No ChangeBar attribute in current module"
}
else
{ atcb = find(b1,"WordDocChangeLog")
if (!null atcb)
{ b1."WordDocChangeLog" = outf
}
}
}
else if (updateChangeBars)
{ updateChangeBars = false
ack "Can only update ChangeBar in current module"
}
Skip skCompAttributes = create
string aName
AttrDef atb1,atb2
int i, iSkip = 0, iElems = noElems (dbeAttrDefNameList)
bool bCheck
for (i=0;i<iElems;i++)
{ bCheck = getCheck (dbeAttrDefNameList, i)
if (bCheck)
{ aName = get (dbeAttrDefNameList, i)
atb1 = find(b1,aName)
atb2 = find(b2,aName)
if ((null atb1) or (null atb2))
{ if (null atb1)
infoBox aName " not in later baseline"
else
infoBox aName " not in earlier baseline"
}
else
put(skCompAttributes, iSkip++, aName)
}
}
current = b1 // make sure filtering is off
filtering off // on both sides.
current = b2
filtering off
current = old
Skip absno1 = getAbsnos b1 // build caches of absnos -> objects
Skip absno2 = getAbsnos b2
Object o1, o2
int iObj=0, iMaxObj=0, diffs=0
bool StartInsert = true
for o1 in absno1 do // loop through side 1
iMaxObj++
progressStart(dbBLCompare, "Baseline Compare", "", iMaxObj)
progressMessage("Compare selected attributes of all objects by Identifier")
for o1 in absno1 do // loop through side 1
{ progressStep(iObj++)
Object o2
int i = (int key absno1)
if (find(absno2, i, o2)) // absno exists in other baseline
{ bool ChangeFound = false
for aName in skCompAttributes do
{ ChangeFound = !compareObjects(i, o1, o2, aName) || ChangeFound
}
ChangeFound = oleInsDel(o1,o2) || ChangeFound
if (!ChangeFound)
{ ChangeFound = oleChange(o1,o2)
}
if (ChangeFound)
{ diffs++ // found a difference
if (updateChangeBars)
{ o1."ChangeBar" = true
}
}
else if (updateChangeBars)
{ o1."ChangeBar" = false
}
delete(absno2, i) // remove from dbeListBaseline
}
else
{ if (StartInsert)
{ outFile << "\\pard\\par\\ul \\fs24 Inserted Objects\\ul0\\par \\par "
StartInsert = false
}
outFile << "\\fs20" identifier(o1) " section " lastHeadingNumber(o1) "\\par "
accept o1
if (updateChangeBars)
{ o1."ChangeBar" = true
}
diffs++
}
}
progressStop()
raise (dbBLCompare)
infoBox "Baseline Compare has finished"
if (StartInsert)
{ outFile << "\\pard\\par\\ul \\fs24 Inserted Objects\\ul0\\par \\par "
}
outFile << "\n\\par\\ul \\fs24 Deleted Objects\\ul0\\par \\par "
for o2 in absno2 do // now we can check for objects not in dbeListCurrent
{ int i = (int key absno2)
outFile << "\\fs20" identifier(o2) " section " lastHeadingNumber(o2) "\\par "
accept o2
diffs++
}
delete absno1 // delete caches
delete absno2
delete skCompAttributes
bool doFilter // set to true if differences
if (diffs==0)
{ outFile << "\n\\par \\fs24 no differences found\\par "
doFilter=false
}
else // set filtering on in baselines
{ if (diffs==1)
outFile << "\n\\par \\fs24 one difference found\\par "
else
outFile << "\n\\par \\fs24 " diffs " differences found\\par "
doFilter=true
}
outFile << "\\par }"
close(outFile)
current = b1 // set filters
if (b1 != old)
close b1
// filtering doFilter
// refresh current
current = b2
if (b2 != old)
close b2
// filtering doFilter
// refresh current
current = old // return to former current module
} // applyCompareFunction
//// MAIN PROGRAM ////////////////////////
Module m = current // check calling context
if (null m)
{ ack "program requires current Module"
halt
}
Baseline b
int i=0
for b in m do // count number of baselines
{ i++
}
if (i==0)
{ ack "no baselines to compare"
halt
}
// Now make a dialog for selecting two baselines for comparison
string where = (current Module)."Name"
dbBLCompare = create "Your Company Baseline Compare V. 1.0 startet at Module: \"" where "\""
string strEmptyArr[] = {}
dbeListCurrent = list(dbBLCompare, "Current/Baseline:", 300, i+1 <? 5, strEmptyArr) // make maximum size of 5 elements
dbeListCurrent->"right"->"unattached" // make lists side by side
dbeListBaseline = list(dbBLCompare, "Baseline to compare with:", 300, i+1 <? 5, strEmptyArr)
dbeListBaseline->"left"->"flush"->dbeListCurrent
dbeListBaseline->"top"->"aligned"->dbeListCurrent
dbeListBaseline->"right"->"unattached"
separator (dbBLCompare)
dbeAttrDefNameList = listView(dbBLCompare, listViewOptionCheckboxes , 250, 14, DUMMY_LIST)
dbeAttrDefNameList->"left"->"form"
dbeAttrDefNameList->"right"->"unattached"
dbeAttrAffects = checkBox(dbBLCompare, "", strAffectsArr, 0)
dbeAttrAffects->"top"->"aligned"->dbeAttrDefNameList
dbeAttrAffects->"left"->"flush"->dbeAttrDefNameList
dbeAttrAffects->"right"->"unattached"
inactive (dbeAttrAffects)
dbeAttrCharacter = checkBox(dbBLCompare, "", strCharacterArr, 0)
dbeAttrCharacter->"top"->"flush"->dbeAttrAffects
dbeAttrCharacter->"left"->"flush"->dbeAttrDefNameList
dbeAttrCharacter->"right"->"unattached"
inactive (dbeAttrCharacter)
dbeAttrValidity = checkBox(dbBLCompare, "valid for: ", strValidityArr, 0)
dbeAttrValidity->"top"->"flush"->dbeAttrCharacter
dbeAttrValidity->"left"->"flush"->dbeAttrDefNameList
dbeAttrValidity->"right"->"unattached"
inactive (dbeAttrValidity)
dbeAttrProperty = checkBox(dbBLCompare, "Properties: ", strPropertyArr, 0)
dbeAttrProperty->"top"->"flush"->dbeAttrValidity
dbeAttrProperty->"left"->"flush"->dbeAttrDefNameList
dbeAttrProperty->"right"->"unattached"
inactive (dbeAttrProperty)
dbeAttrType = field (dbBLCompare, "Type: ", "", 27, true)
dbeAttrType->"top"->"spaced"->dbeAttrProperty
dbeAttrType->"left"->"flush"->dbeAttrDefNameList
dbeAttrType->"right"->"unattached"
dbeDefaultValue = field (dbBLCompare, "Default value:", "", 21, true)
dbeDefaultValue->"top"->"flush"->dbeAttrType
dbeDefaultValue->"left"->"flush"->dbeAttrDefNameList
dbeDefaultValue->"right"->"unattached"
dbeAttrTypeList = text(dbBLCompare, "Possible values or defined borders:", "", 215, 142, true)
dbeAttrTypeList->"top"->"spaced"->dbeDefaultValue
dbeAttrTypeList->"left"->"flush"->dbeAttrDefNameList
dbeAttrTypeList->"right"->"unattached"
//dbeAttrTypeList->"bottom"->"unattached"
dbeAttrTypeSelect = toggle(dbBLCompare,"show System and Hidden Attributes", true)
dbeAttrTypeSelect->"top"->"flush"->dbeAttrDefNameList
dbeAttrTypeSelect->"left"->"form"
dbeAttrTypeSelect->"right"->"unattached"
dbeAttrTypeSelect->"bottom"->"unattached"
separator (dbBLCompare)
dbeOutFileLabel = label(dbBLCompare, "Output to:")
dbeOutFileBrowse = fileName(dbBLCompare, "D:\\Project - DOORS Demonstration\\BL_Comp_Result.rtf", "*.rtf", "Rich Text files", false)
dbeChangeBarToggle = toggle(dbBLCompare,"Update ChangeBar attribute", false)
beside (dbBLCompare)
dbeShowDiffToggle = toggle(dbBLCompare,"Show Object Text changes as markup", true)
// dummy
DBE dbeDummy = label(dbBLCompare, "")
dbeDummy->"top"->"spaced"->dbeShowDiffToggle
dbeDummy->"left"->"form"
// Copyright
DBE dbeCopyRight = label(dbBLCompare, "Copyright (C) Your Company Your Town 2005-2007 Confidential. All rights reserved.")
dbeCopyRight->"bottom"->"spaced"->dbeDummy
dbeCopyRight->"left"->"form"
apply(dbBLCompare, "Compare Now", applyCompareFunction)
set(dbeAttrDefNameList, cbDoSetAttrTypes, doNothing, doNothing)
set(dbeAttrTypeSelect, cbSystemSelect)
realize (dbBLCompare, 0, 0) // we realize so that the lists can be populated using insert
insertColumn(dbeAttrDefNameList, 0, "Attribute Name", 200, iconNone)
for b in m do // fill up the baselines skip list with current baselines
{ string str = (major b) "." (minor b) (suffix b)
put(skBaselines, b, str)
insert(dbeListCurrent, 0, str)
insert(dbeListBaseline, 0, str)
}
insert(dbeListCurrent, 0, "current") // put current at head of lists
set(dbeListCurrent, 0)
insert(dbeListBaseline, 0, "current")
set(dbeListBaseline, 1)
showAttributes()
show dbBLCompare // off we go.......
// end of BaselineCompare.dxl
I have code similar to:
use std::string::{String};
use std::vec::{Vec};
enum State {
A {
n: usize,
lines: Vec<String>,
},
B {
n: usize,
}
}
fn main() {
use State::*;
let lines = vec!["a", "b", "GO", "c", "GO", "d"];
let mut state = B { n: 0 };
for line in &lines {
state = match state {
A { n, lines } => {
if line == &"GO" {
B { n: n + 1 }
} else {
let mut new_lines = Vec::from(lines);
new_lines.push(line.to_string());
A { n: n, lines: new_lines }
}
},
B { n } => {
A { n: n, lines: vec![line.to_string()] }
},
};
}
let final_n = match state {
A { n, .. } => n,
B { n } => n,
};
println!("final_n = {}", final_n);
}
Rust Playground link: http://is.gd/0QTYaQ
(Note that this is a simplification of the actual code. See the first revision of this question for the full background.)
I want to avoid creating the new_lines vector, so I tried binding the State::A value to a variable and accessing the fields of the value like so:
s # A { .. } => {
if line == &"GO" {
B { n: s.n + 1 }
} else {
s.lines.push(line.to_string());
s
}
},
However, this fails to compile:
ParseState_enum_test.rs:23:28: 23:31 error: attempted access of field `n` on type `State`, but no field with that name was found
ParseState_enum_test.rs:23 B { n: s.n + 1 }
^~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
ParseState_enum_test.rs:25:21: 25:28 error: attempted access of field `lines` on type `State`, but no field with that name was found
ParseState_enum_test.rs:25 s.lines.push(line.to_string());
^~~~~~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
error: aborting due to 2 previous errors
How do I access the fields of the value bound to the variable?
EDIT: I am aware of ref mut in pattern binding, but I don't think that this is a good solution in my case. If I use ref mut, then I need to create a clone of the vector because this code does not compile:
A { n, ref mut lines } => {
if line == &"GO" {
B { n: n + 1 }
} else {
lines.push(line.to_string());
A {
n: n,
lines: lines, // error: mismatched types
}
}
},
The following seems to work. Does it solve the problem?
let new_state = match state {
B {n} => A { n: n, lines: vec![line.to_string()] },
A {n, mut lines} => {
match *line {
"GO" => B { n: n + 1 },
_ => {
lines.push(line.to_string());
A{ n:n, lines: lines}
}
}
}
};
state = new_state
https://play.rust-lang.org/?gist=4fa712834999e45ccd4d&version=stable
Let's look at a much simpler version of your issue:
enum Foo {
Alpha { score: u8 },
Beta { lives_left: u8 },
}
fn main() {
let the_value = Foo::Alpha { score: 42 };
match the_value {
alpha_only # Alpha => println!("Score is {}", alpha_only.score),
_ => println!("Dunno what to do!"),
}
}
The problem is that enum variants are not standalone types. That is, there is no way to have a variable of type Foo::Alpha; you can only have it be the type Foo. You can see this in the error message:
attempted access of field score on type Foo, but no field with that name was found
When you use # to bind the entire pattern, you can only know that you are getting something of type Foo.
The normal way of dealing with this is to bind to a component of the item using ref:
match the_value {
Foo::Alpha { ref score } => println!("Score is {}", score),
_ => println!("Dunno what to do!"),
}
And if you need to mutate the value, use ref mut:
match the_value {
Foo::Alpha { ref mut score } => {
*score += 1;
println!("Score is {}", score)
},
_ => println!("Dunno what to do!"),
}
Of if you can consume the value, you don't need ref:
let the_value = Foo::Alpha { score: 42 };
let new_value = match the_value {
Foo::Alpha { score } => Foo::Alpha { score: score + 1 },
Foo::Beta { lives_left } => Foo::Alpha { score: lives_left * 2 },
};